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接口的源码中没有任何方法。
public static voidgetTypeParameters(){
List list = new ArrayList();
Map map = new HashMap();
System.out.println(Arrays.toString(list.getClass().getTypeParameters())); //[E]
System.out.println(Arrays.toString(map.getClass().getTypeParameters())); //[K, V]
}/*** 其中最关键的差别是本节的变量声明多了一对大括号
* 变量声明其实是创建了一个匿名内部类, 这个类是 HashMap 的子类
*
* Java 引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类。那我们其实就可以通过定义类的方式,在
* 类信息中保留泛型信息,从而在运行时获得这些泛型信息
*
* 简而言之,Java 的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的
**/
public static voidgetActualTypeArguments(){//Map map = new HashMap() ;
Map map = new HashMap() {};
Type type=map.getClass().getGenericSuperclass();
ParameterizedType parameterizedType= ParameterizedType.class.cast(type);for(Type typeArgument : parameterizedType.getActualTypeArguments()) {
System.out.println(typeArgument.getTypeName()); //java.lang.String, java.lang.Integer
}
}