反射相关接口
下面就把Type的来龙去脉彻底弄清楚
Type
Type
是所有类型的父接口, 如原始类型(raw types,对应Class)、 参数化类型(parameterized types, 对应ParameterizedType
)、 数组类型(array types,对应GenericArrayType
)、 类型变量(type variables, 对应TypeVariable)和基本(原生)类型(primitive types, 对应Class), 子接口有ParameterizedType
, TypeVariable
, GenericArrayType
, WildcardType
, 实现类有Class
Class
getGenericSuperclass()
:获取某个类继承的父类的类型(返回Type)getGenericInterfaces()
:获取某个类实现的所有接口的类型,返回的是接口的类型数组(Type[])
1 | package com.calebzhao.test; |
ParameterizedType
参数化类型, 如下面的这些都是泛型:
1 | Map |
而类似于下面这样的不是 ParameterizedType:
1 | Set set; |
ParameterizedType 的几个主要方法如下:
Type getRawType()
: 返回承载该泛型信息的对象, 如上面那个Map
承载范型信息的对象是MapType[] getActualTypeArguments()
: 返回实际泛型类型列表, 如上面那个Map
实际范型列表中有两个元素, 都是String
Type getOwnerType()
: 这个比较少用到,返回的是这个 ParameterizedType 所在的类的 Type (注意当前的 ParameterizedType 必须属于所在类的 member), 比如Map map
这个 ParameterizedType 的 getOwnerType() 为 null,而Map.Entryentry
的 getOwnerType() 为 Map类的Type。示例1:
1 | package com.calebzhao.test; |
- 示例2
1 | package com.calebzhao.test; |
控制台输出:
TypeVariable
类型变量, 泛型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型前的信息.
它的声明是这样的: public interface TypeVariable
也就是说它跟GenericDeclaration
有一定的联系, 我是这么理解的:TypeVariable
是指在GenericDeclaration
中声明的
、
这些东西中的那个变量T、C; 它有如下方法:
Type[] getBounds()
: 获取类型变量的上边界, 若未明确声明上边界则默认为ObjectD getGenericDeclaration()
: 获取声明该类型变量实体String getName()
: 获取在源码中定义时的名字
注意:
- 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用
super
; - 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class TestType <K extends Comparable & Serializable, V> {
K key;
V value;
public static void main(String[] args) throws Exception {
// 获取字段的类型
Field fk = TestType.class.getDeclaredField("key");
Field fv = TestType.class.getDeclaredField("value");
Assert.that(fk.getGenericType() instanceof TypeVariable, "必须为TypeVariable类型");
Assert.that(fv.getGenericType() instanceof TypeVariable, "必须为TypeVariable类型");
TypeVariable keyType = (TypeVariable)fk.getGenericType();
TypeVariable valueType = (TypeVariable)fv.getGenericType();
// getName 方法
System.out.println(keyType.getName()); // K
System.out.println(valueType.getName()); // V
// getGenericDeclaration 方法
System.out.println(keyType.getGenericDeclaration()); // class com.test.TestType
System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
// getBounds 方法
System.out.println("K 的上界:"); // 有两个
for (Type type : keyType.getBounds()) { // interface java.lang.Comparable
System.out.println(type); // interface java.io.Serializable
}
System.out.println("V 的上界:"); // 没明确声明上界的, 默认上界是 Object
for (Type type : valueType.getBounds()) { // class java.lang.Object
System.out.println(type);
}
}
}GenericArrayType
泛型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedType
或TypeVariable
类型,它只有一个方法:
Type getGenericComponentType()
: 返回数组的组成对象, 即被JVM编译后实际的对象
1 | public class TestType <T> { |
- 第一个参数
List
的组成元素[] List
是ParameterizedType类型, 打印结果为true - 第二个参数T[]的组成元素T是
TypeVariable
类型, 打印结果为true - 第三个参数
List
不是数组, 打印结果为false - 第四个参数
String[]
的组成元素String
是普通对象, 没有范型, 打印结果为false - 第五个参数
int[] pTypeArray
的组成元素int是原生类型, 也没有范型, 打印结果为false
WildcardType
通配符泛型, 比如? extends Number
和 ? super Integer
它有如下方法:
Type[] getUpperBounds()
: 获取范型变量的上界Type[] getLowerBounds()
: 获取范型变量的下界
注意:
现阶段通配符只接受一个上边界或下边界, 返回数组是为了以后的扩展, 实际上现在返回的数组的大小是1
1 | public class TestType { |
再写几个边界的例子:
List
, 上界为class java.lang.Number
, 属于Class
类型List>
, 上界为java.util.List
, 属于ParameterizedType
类型List>
, 上界为java.util.List
, 属于ParameterizedType
类型List
, 上界为T, 属于TypeVariable类型List
, 上界为T[], 属于GenericArrayType
类型
它们最终统一成Type
作为数组的元素类型
原子类型
1 | public class TypeTest { |
Type及其子接口的来历
泛型出现之前的类型
没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class
类进行抽象。Class
类的一个具体对象就代表一个指定的原始类型。泛型出现之后的类型
泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。与泛型有关的类型不能和原始类型统一到
Class
的原因
产生泛型擦除的原因
原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。Java中如何引入泛型
为了使用泛型又不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。Class
不能表达与泛型有关的类型
因此,与泛型有关的参数化类型、类型变量类型、限定符类型 、泛型数组类型这些类型编译后全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型对应的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class
类中。与泛型有关的类型在Java中的表示
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType
,TypeVariable
, GenericArrayType,WildcardType
几种类型来代表不能被归一到Class
类中的类型但是又和原始类型齐名的类型。引入
Type
的原因
为了程序的扩展性,最终引入了Type
接口作为Class
和ParameterizedType
,TypeVariable
,GenericArrayType
,WildcardType
这几种类型的总的父接口。这样可以用Type
类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class
Type
接口中没有方法的原因
从上面看到,Type
的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type
接口的源码中没有任何方法。