在 Java 中, 泛型与反射是两个重要的概念, 我们几乎能够经常的使用到它们. 而谈起 Type, 如果有人还比较陌生的话 , 那么说起一个它的直接实现类 --Class 的话, 大家都应该明白了. Type 是 Java 语言中所有类型的公共父接口. 而这篇文章, 主要是讲述了 Type 的其它四个子类 --ParameterizedType, TypeVariable,GenericArrayType,WildcardType. 对想了解这几个类的朋友一个参考
ParameterizedType: 参数化类型
参数化类型即我们通常所说的泛型类型, 一提到参数, 最熟悉的就是定义方法时有形参, 然后调用此方法时传递实参. 那么参数化类型怎么理解呢? 顾名思义, 就是将类型由原来的具体的类型参数化, 类似于方法中的变量参数, 此时类型也定义成参数形式(可以称之为类型形参), 然后在使用 / 调用时传入具体的类型(类型实参). 那么我们的 ParameterizedType 就是这样一个类型, 下面我们来看看它的三个重要的方法:
getRawType(): Type
该方法的作用是返回当前的 ParameterizedType 的类型. 如一个 List, 返回的是 List 的 Type, 即返回当前参数化类型本身的 Type.
getOwnerType(): Type
返回 ParameterizedType 类型所在的类的 Type. 如 Map.Entry<String, Object > 这个参数化类型返回的事 Map(因为 Map.Entry 这个类型所在的类是 Map)的类型.
getActualTypeArguments(): Type[]
该方法返回参数化类型 <> 中的实际参数类型, 如 Map<String,Person> map 这个 ParameterizedType 返回的是 String 类, Person 类的全限定类名的 Type Array. 注意: 该方法只返回最外层的 <> 中的类型, 无论该 <> 内有多少个<>.
下面让我们用一段例子来看一下具体的用法:
- // 是 ParameterizedType
- private HashMap<String, Object> map;
- private HashSet<String> set;
- private List<String> list;
- private Class<?> clz;
- // 不是 ParameterizedType
- private Integer i;
- private String str;
- private static void printParameterizedType(){
- Field[] fields = TestParameterizedTypeBean.class.getDeclaredFields();
- for (Field f : fields){
- // 打印是否是 ParameterizedType 类型
- System.out.println("FieldName:" + f.getName() + "instanceof ParameterizedType is :" +
- (f.getGenericType() instanceof ParameterizedType));
- }
- // 取 map 这个类型中的实际参数类型的数组
- getParameterizedTypeWithName("map");
- getParameterizedTypeWithName("str");
- }
- private static void getParameterizedTypeWithName(String name){
- Field f;
- try {
- // 利用反射得到 TestParameterizedTypeBean 类中的所有变量
- f = TestParameterizedTypeBean.class.getDeclaredField(name);
- f.setAccessible(true);
- Type type = f.getGenericType();
- if (type instanceof ParameterizedType){
- for(Type param : ((ParameterizedType)type).getActualTypeArguments()){
- // 打印实际参数类型
- System.out.println("---type actualType---" + param.toString());
- }
- // 打印所在的父类的类型
- System.out.println("---type ownerType0---"+ ((ParameterizedType) type).getOwnerType());
- // 打印其本身的类型
- System.out.println("---type rawType---"+ ((ParameterizedType) type).getRawType());
- }
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- }
- }
上面的代码主要是定义了一些变量, 这些变量中间有 ParameterizedType 也有普通类型变量, 我们来看一下上述代码的输出:
TypeVariable: 类型变量
范型信息在编译时会被转换为一个特定的类型, 而 TypeVariable 就是用来反映在 JVM 编译该泛型前的信息.(通俗的来说, TypeVariable 就是我们常用的 T,K 这种泛型变量)
getBounds(): Type[]:
返回当前类型的上边界, 如果没有指定上边界, 则默认为 Object.
getName(): String:
返回当前类型的类名
getGenericDeclaration(): D
返回当前类型所在的类的 Type.
下面通过一个例子来加深了解:
- public class TestTypeVariableBean<K extends Number, T> {
- //K 有指定了上边界 Number
- K key;
- //T 没有指定上边界, 其默认上边界为 Object
- T value;
- public static void main(String[] args){
- Type[] types = TestTypeVariableBean.class.getTypeParameters();
- for (Type type : types){
- TypeVariable t = (TypeVariable) type;
- int index = t.getBounds().length - 1;
- // 输出上边界
- System.out.println("--getBounds()--" + t.getBounds()[index]);
- // 输出名称
- System.out.println("--getName()--" + t.getName());
- // 输出所在的类的类型
- System.out.println("--getGenericDeclaration()--" + t.getGenericDeclaration());
- }
- }
- }
再来看下输出:
GenericArrayType: 泛型数组类型:
组成数组的元素中有泛型则实现了该接口; 它的组成元素是 ParameterizedType 或 TypeVariable 类型.(通俗来说, 就是由参数类型组成的数组. 如果仅仅是参数化类型, 则不能称为泛型数组, 而是参数化类型). 注意: 无论从左向右有几个 [] 并列, 这个方法仅仅脱去最右边的 [] 之后剩下的内容就作为这个方法的返回值.
getGenericComponentType(): Type:
返回组成泛型数组的实际参数化类型, 如 List[] 则返回 List.
下面还是通过一个例子来深入了解:
- public class TestGenericArrayTypeBean<T> {
- // 泛型数组类型
- private T[] value;
- private List<String>[] list;
- // 不是泛型数组类型
- private List<String> singleList;
- private T singleValue;
- public static void main(String[] args){
- Field[] fields = TestGenericArrayTypeBean.class.getDeclaredFields();
- for (Field field: fields){
- field.setAccessible(true);
- // 输出当前变量是否为 GenericArrayType 类型
- System.out.println("Field:"
- + field.getName()
- + "; instanceof GenericArrayType"
- + ":"
- + (field.getGenericType() instanceof GenericArrayType));
- if (field.getGenericType() instanceof GenericArrayType){
- // 如果是 GenericArrayType, 则输出当前泛型类型
- System.out.println("Field:"
- + field.getName()
- + "; getGenericComponentType()"
- + ":"
- + (((GenericArrayType) field.getGenericType()).getGenericComponentType()));
- }
- }
- }
- }
接下来看下输出:
WildcardType: 通配符类型
表示通配符类型, 比如 <?>, <? Extends Number > 等
getLowerBounds(): Type[]: 得到下边界的数组
getUpperBounds(): Type[]: 得到上边界的 type 数组
注: 如果没有指定上边界, 则默认为 Object, 如果没有指定下边界, 则默认为 String
下面还是通过一个例子了解一下:
- public class TestWildcardType {
- public static void main(String[] args){
- // 获取 TestWildcardType 类的所有方法(本例中即 testWildcardType 方法)
- Method[] methods = TestWildcardType.class.getDeclaredMethods();
- for (Method method: methods){
- // 获取方法的所有参数类型
- Type[] types = method.getGenericParameterTypes();
- for (Type paramsType: types){
- System.out.println("type:" + paramsType.toString());
- // 如果不是参数化类型则直接 continue, 执行下一个循环条件
- if (!(paramsType instanceof ParameterizedType)){
- continue;
- }
- // 将当前类型强转为参数化类型并获取其实际参数类型(即含有通配符的泛型类型)
- Type type = ((ParameterizedType) paramsType).getActualTypeArguments()[0];
- // 输出其是否为通配符类型
- System.out.println("type instanceof WildcardType :" +
- ( type instanceof WildcardType));
- if (type instanceof WildcardType){
- int lowIndex = ((WildcardType) type).getLowerBounds().length - 1;
- int upperIndex = ((WildcardType) type).getUpperBounds().length - 1;
- // 输出上边界与下边界
- System.out.println("getLowerBounds():"
- +
- (lowIndex>= 0 ? ((WildcardType) type).getLowerBounds()[lowIndex] : "String")
- + "; getUpperBounds():"
- +
- (upperIndex>=0 ? ((WildcardType) type).getUpperBounds()[upperIndex]:"Object"));
- }
- }
- }
- }
- public void testWildcardType(List<? extends OutputStream> numberList, List<? super InputStream> upperList, List<Integer> list, InputStream inputStream){}
- }
输出:
泛型的擦除的原因以及 Java 中 Type 的作用
其实在 jdk1.5 之前 Java 中只有原始类型而没有泛型类型, 而在 JDK 1.5 之后引入泛型, 但是这种泛型仅仅存在于编译阶段, 当在 JVM 运行的过程中, 与泛型相关的信息将会被擦除, 如 List 与 List 都将会在运行时被擦除成为 List 这个类型. 而类型擦除机制存在的原因正是因为如果在运行时存在泛型, 那么将要修改 JVM 指令集, 这是非常致命的.
此外, 原始类型在会生成字节码文件对象, 而泛型类型相关的类型并不会生成与其相对应的字节码文件(因为泛型类型将会被擦除), 因此, 无法将泛型相关的新类型与 class 相统一. 因此, 为了程序的扩展性以及为了开发需要去反射操作这些类型, 就引入了 Type 这个类型, 并且新增了 ParameterizedType, TypeVariable, GenericArrayType, WildcardType 四个表示泛型相关的类型, 再加上 Class, 这样就可以用 Type 类型的参数来接受以上五种子类的实参或者返回值类型就是 Type 类型的参数. 统一了与泛型有关的类型和原始类型 Class. 而且这样一来, 我们也可以通过反射获取泛型类型参数.
来源: https://juejin.im/post/5adefaba518825670e5cb44d