Java中的Type类型详解
概述
在JDK1.5之前之一原始类型。此时,所有的原始类型都通过字节码文件类Class进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
JDK1.5加入了泛型类,扩充了数据类型,从只有原始类型基础上扩充了 参数化类型、类型变量类型、通配符类型、泛型数组类型。
Type是Java语言中所有类型(Class)的公共父接口。
Type类关系图
从做到右依次是:Class(原始/基本类型):实现了Type接口,GenericArrayType(泛型数组类型),ParameterizedType(参数化类型),WildcardType(通配符类型),TypeVariable(类型变量)。
- Class:不仅包括我们平常所指的类、枚举、数组、注解,还包括基本类型int、float等等。
- TypeVariable:比如List 中的T等。
- WildcardType:也叫做泛型表达式类型,例如List<? extends Number> 这种。
- ParameterizedType:就是我们平常所用到的泛型List、Map(注意和TypeVariable的区别,参数化类型表示的是List这样的一个整体而不是T)。
- GenericArrayType:泛型数组类型,并不是我们工作中所使用的数组String[]、Byte[],这些都是Class,而是带有泛型的数组,即T[]。
Type接口本身算是一个标记接口,不提供任何需要复写的方法。
ParameterizedType 参数化类型
参数化类型,即平常用的泛型;例如:List、Map<K,V>等带有参数化的对象。
如何理解呢?
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化(比如:List,T就是类型参数),类似于方法中的参数变量,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
public interface ParameterizedType extends Type {// 获取类型内部的参数化类型,比如Map<K,V>里面的K,V类型;// 注意该方法只返回最外层的<>中的类型,无论该<>内有多少个<>。Type[] getActualTypeArguments();// 类的原始类型,一般都是ClassType getRawType();// 获取所有者类型(只有内部类才有所有者,比如Map.Entry的所有者就是Map),如果不是内部类,返回null。Type getOwnerType();
}
具体用法
@Slf4j
public class ParameterizedTypeTest {private Map<String, ParameterizedTypeTest> map;private Set<String> setStr;private Class<?> clz;private Holder<String> holder;private List<String> listStr;private ArrayList<String> arrayList;private Map.Entry<String, String> entry;// 非参数化类型private String str;private Integer i;private Set set;private List list;static class Holder<V> {}public static void main(String[] args) {try {// 拿到所有的字段Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();Arrays.stream(fields).forEach(item -> {if (item.getGenericType() instanceof ParameterizedType) {item.setAccessible(true);ParameterizedType parameterizedType = (ParameterizedType) item.getGenericType();log.info("{} :", item.getName());log.info("getActualTypeArguments: {}", Arrays.asList(parameterizedType.getActualTypeArguments()));log.info("getRawType: {}", parameterizedType.getRawType());log.info("getOwnerType: {}", parameterizedType.getOwnerType());} else {log.info("{} is not ParameterizedType", item.getName());}});} catch (Exception e) {log.info("exception:", e);}}
}
输出:
map :
getActualTypeArguments: [class java.lang.String, class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest]
getRawType: interface java.util.Map
getOwnerType: null
setStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.Set
getOwnerType: null
clz :
getActualTypeArguments: [?]
getRawType: class java.lang.Class
getOwnerType: null
holder :
getActualTypeArguments: [class java.lang.String]
getRawType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest$Holder
getOwnerType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest
listStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.List
getOwnerType: null
arrayList :
getActualTypeArguments: [class java.lang.String]
getRawType: class java.util.ArrayList
getOwnerType: null
entry :
getActualTypeArguments: [class java.lang.String, class java.lang.String]
getRawType: interface java.util.Map$Entry
getOwnerType: interface java.util.Map
str is not ParameterizedType
i is not ParameterizedType
set is not ParameterizedType
list is not ParameterizedType
先看后面几个,发现即使是List,但是我们没给与泛型,它也不是ParameterizedType参数化类型,因此如果想成为泛型类型必须要指定泛型参数才行。
TypeVariable 类型变量
泛型信息在编译时会被转换成一个特定的类型,而TypeVariable就是用来反映在JVM编译该泛型前的信息。(通俗的讲,TypeVariable就是我们常用的List 、Map<K,V>中的T,K这种泛型变量)。
还可以对类型变量加上extends限定,这样会有类型变量对应的上限;值得注意的是,类型变量的上限可以有多个,必须使用&连接,例如:
List<T extends Number & Serializable>,其中&后必须是接口:
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {// 类型变量对应的上边界,如果没有指定上限,返回Object 可以有多个Type[] getBounds();// 获取类型变量所在类的Type,比如TypeVariableTest<T>类,getGenericDeclaration()得到的就是TypeVariableTest。D getGenericDeclaration();// 获取类型变量在源码中定义的名称String getName();// JDK8新增的,获取注解类型的上限数组AnnotatedType[] getAnnotatedBounds();
}
具体用法
public class TypeVariableTest<K extends Number, T> {// K有指定上边界 Numberprivate K key;// T没有指定上边界,其默认上边界为Objectprivate T value;public static void main(String[] args) {TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();for (TypeVariable<Class<TypeVariableTest>> type : typeParameters) {int index = type.getBounds().length - 1;// 输出上边界System.out.println("---getBounds()-- " + type.getBounds()[index]);// 输出名称System.out.println("---getName()-- " + type.getName());// 输出所在的类的类型System.out.println("---getGenericDeclaration()-- " + type.getGenericDeclaration());}}
}
输出:
---getBounds()-- class java.lang.Number
---getName()-- K
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest
---getBounds()-- class java.lang.Object
---getName()-- T
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest
GenericArrayType 泛型数组类型
描述的是形如:A[](参数化类型数组)或者T[](类型变量数组)。
它的组成元素是ParameterizedType或TypeVariable类型。
无论从左向右右几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。
public interface GenericArrayType extends Type {// 返回泛型数组中成员类型,即List<String>[]中的List<String>Type getGenericComponentType();
}
具体用法
public class GenericArrayTypeTest<T> {// 泛型数组类型private T[] value;private List<String>[] lists;// 非泛型数组类型private List<String> list;private T singleValue;public static void main(String[] args) {Field[] declaredFields = GenericArrayTypeTest.class.getDeclaredFields();Arrays.stream(declaredFields).forEach(field -> {field.setAccessible(true);// 输出当前变量是否为GenericArrayType类型System.out.println("Field: " + field.getName() + "; instanceof GenericArrayType: "+ (field.getGenericType() instanceof GenericArrayType));if (field.getGenericType() instanceof GenericArrayType) {// 输出泛型类型System.out.println("Field: " + field.getName() + "; getGenericComponentType():"+ ((GenericArrayType) field.getGenericType()).getGenericComponentType());}field.setAccessible(false);});}
}
输出:
Field: value; instanceof GenericArrayType: true
Field: value; getGenericComponentType():T
Field: lists; instanceof GenericArrayType: true
Field: lists; getGenericComponentType():java.util.List<java.lang.String>
Field: list; instanceof GenericArrayType: false
Field: singleValue; instanceof GenericArrayType: false
WildCardType 通配符类型
表示通配符类型,比如<?>,<? extends Number>等。
如果没有指定上边界,则默认Object,如果没有指定下边界,则默认为String。
public interface WildcardType extends Type {// 获得泛型表达式上边界,表达式中使用extendsType[] getUpperBounds();// 或者泛型表达式的下边界,表达式中使用superType[] getLowerBounds();
}
具体用法
public class WildcardTypeTest {// 使用通配符类型参数的方法public void testWildcardType(List<? extends OutputStream> outputStreams, List<? super InputStream> inputStreams,List<Integer> list, InputStream inputStream) {}public static void main(String[] args) {// 获取WildcardTypeTest类的所有方法(本例中是testWildcardType方法)Method[] declaredMethods = WildcardTypeTest.class.getDeclaredMethods();for (Method method : declaredMethods) {System.out.println("method name: " + method.getName());// 获取方法的所有参数类型Type[] genericParameterTypes = method.getGenericParameterTypes();for (Type type : genericParameterTypes) {System.out.println("type: " + type.toString());// 如果不是参数化类型则直接continueif (!(type instanceof ParameterizedType)) {continue;}// 将当前类型强转为参数化类型并获取实际参数(即含有通配符的泛型类型)Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];// 输出其是否为通配符类型System.out.println("type instanceof WildcardType: "+ (actualTypeArgument instanceof WildcardType));if (actualTypeArgument instanceof WildcardType) {int lowIndex = ((WildcardType) actualTypeArgument).getLowerBounds().length - 1;int upperIndex = ((WildcardType) actualTypeArgument).getUpperBounds().length - 1;// 输出上边界与下边界System.out.println("getLowerBounds(): " +(lowIndex >= 0 ? ((WildcardType) actualTypeArgument).getLowerBounds()[lowIndex] : "String")+ ";getUpperBounds(): " +(upperIndex >= 0 ? ((WildcardType) actualTypeArgument).getUpperBounds()[upperIndex] : "Object"));}}}}
}
输出:
method name: main
type: class [Ljava.lang.String;
method name: testWildcardType
type: java.util.List<? extends java.io.OutputStream>
type instanceof WildcardType: true
getLowerBounds(): String;getUpperBounds(): class java.io.OutputStream
type: java.util.List<? super java.io.InputStream>
type instanceof WildcardType: true
getLowerBounds(): class java.io.InputStream;getUpperBounds(): class java.lang.Object
type: java.util.List<java.lang.Integer>
type instanceof WildcardType: false
type: class java.io.InputStream
表达式中没有指定上限,默认都是有上限class java.lang.Object,但是没有默认下线。
泛型中使用&(并且)操作符
我们有时候可以看到泛型搭配上 & 的使用方式,比如:
public <R extends Enum<R> & BaseIntEnum> List<R> parse2Enums(...){...}
说明一点:& 不能用于 ? 通配符上(因为通配符不能放在泛型的申明上),& 只能放在泛型的声明上,比如类似这种:
// 泛型类上申明,约束泛型类变量class WildcardTypeT<T extends Comparable<T> & List<T> & Serializable> {}// 方法上申明public <R extends Enum<R> & Serializable> List<R> parse2Enums(){}
需要注意的是,&后面只能放置接口,不能是具体的类型,即使是Object也不行。
因此当需要多重约束的时候,可以使用&操作符,但是它不能用于super上 ,因为java有规定:
// 合法的class A<T extends Number & Serializable> {}// 不合法的class B<T super Number & Serializable>
原因参考:https://stackoverflow.com/questions/37411256/why-super-keyword-in-generics-is-not-allowed-at-class-level
与泛型有关的类型不能和原始类型统一到Class的原因
产生泛型擦除的原因
- 原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分,如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的(简单的说就是Java要向下兼容,所以它的泛型是个加东西,只是语法层面的技术)。
- 泛型仅仅存在于编译阶段,当在JVM运行的过程中,于泛型相关的信息将会被擦除,如果List于List都将会在运行时被擦除成List这个类型。而类型擦除机制存在的原因正是因为如果在运行时存在泛型,那么将要修改JVM指令集,这是非常致命的。
- 我们其实可以通过定义类的方式,在类信息中保留泛型信息,从而在运行时获得这些泛型信息。
简而言之,Java的泛型擦除是有范围的,即类定义中的泛型是 不会 被擦除的。
引入Type的原因
原始类型会生成字节码文件对象,而泛型相关的类型不会生成与其相对应的字节码文件(因为泛型类型将会被擦除),因此,无法将泛型相关的新类型与class相统一。
因此为了程序的扩展性以及为了开放需要去反射操作这些类型,就引入了Type这个类型,并且新增了ParameterizedType、TypeVariable、GenericArrayType、WildcardType四个表示泛型相关的类型,再加上Class,这样就可以用Type类型的的参数来接收以上5种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class。
总结
- Type是JDK5开始引入的,其引入主要是为了泛型,没有泛型之前,只有所谓的原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
- 泛型出现之后,也就扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、泛型数组类型,也就是Type的子接口。
- 那为什么没有统一到Class下,而是增加了一个Type呢?(Class是种类的意思,Type是类型的意思)。
- 是为了程序的扩展性,最终引入了Type接口作为Class、ParameterizedType、GenericArrayType、TypeVariable和WildcardType这几种类型的总的父接口。
- 这样实现Type类型参数接受以上五种子类实参或者返回值类型就是Type类型的参数,Type作为媒介统一处理原始类型和泛型相关类型。
List<T ? extends String>[]:这里List<T ? extends String>就是ParameterizedType,T是TypeVariable,T ? extends String是WildcardType(注意,WildcardType不是java类型,而是一个表达式),整个List<T ? extends String>[]就是GenericArrayType。
参考
https://cloud.tencent.com/developer/article/1497707
https://juejin.cn/post/6844903597977632776
Java中的Type类型详解相关推荐
- 1.Type类型详解
文章目录 1.Type类型 1.1 什么是Type? 1.2 Type的用处 1.3 ParmeterizedType(参数化类型)的作用 1.3.1 测试ParmeterizedType 1.3.2 ...
- Java中的运算符——赋值运算符详解
赋值运算符主要是这几种:=.+=.-=.*=./=.%=,其它和二进制相关的内容这里就不详细解释了.赋值类的运算符包括基本赋值运算符(=)和扩展的赋值运算符(+=.-=.*=./=.%=).我们来看一 ...
- Java中的static关键字详解
** Java中的static关键字详解 ** 在一个类中定义一个方法为static,即静态的,那就是说无需本类的对象就可以调用此方法.调用一个静态方法就是 "类名.方法名" ,静 ...
- Java中的main()方法详解
源文作者:leizhimin 源文链接:http://lavasoft.blog.51cto.com/62575/53263 源文作者版权申明: 版权声明:原创作品,允许转载,转载时请务必以超链 ...
- mysql binlog event_MySQL binlog中的事件类型详解
MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型. ...
- java中list和map详解
java中list和map详解 一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedL ...
- stringtokenizer java_基于Java中的StringTokenizer类详解(推荐)
StringTokenizer是字符串分隔解析类型,属于:Java.util包. 1.StringTokenizer的构造函数 StringTokenizer(String str):构造一个用来解析 ...
- Burp suite—Intruder中Attack Type模式详解
Burp suite-Intruder中Attack Type模式详解 提要:Burp suite集成工具中的Intruder模块在日常的使用的安全测试中会经常频繁使用,Intruder中的攻击类型( ...
- Java中的运算符——逻辑运算符详解
逻辑运算符主要包括逻辑与( & ),逻辑或( | ),逻辑异或( ^ ),短路与( && ),短路或( || ).所有逻辑运算符的特点是操作数都是布尔类型,并且最终的运算结果也 ...
最新文章
- 10大高性能开发宝石,我要消灭一半程序员!
- java通过按钮打开新窗口_如何在java程序中,当点击一个按钮后,关闭当前窗口,开启一个新的窗口。...
- GDCM:无效的DICOM文件的测试程序
- Ubuntu 9.04下安装source navigator---linux下看代码工具
- AppiumDriver java部分api
- android daemon 程序,(转)Android App Daemon
- 仍然报错_only_full_group_by配置,竟让所有应用报错?
- 实现API接口调用--来源阿里云大学-归档
- Python邻接矩阵邻接表转换
- LG电子成功进行太赫兹频段6G无线信号传输,距离超过100米
- Html中 table,list等表格 中 js 的 Checkbox全选,反选,单选,获取数据选中行 的写法
- logstash关于date时间处理的几种方式总结
- FFmpeg代码实现视频转jpg图片
- 新的开始——参加培训
- 富文本编辑器 可全屏可粘贴(只能单独粘贴图片或文字)
- 计算机应用基础2004版,计算机应用基础试题2004年6月
- 腾讯、百度、高德、谷歌、天地图地图瓦片
- g代码生成器 源代码_如何生成源代码?
- 索尼的hlg是什么_hlg(索尼hlg和slog的区别)
- Android网络框架Volley项目实战-刘桂林-专题视频课程