> 文章列表 > Java Type接口出现的原因以及它和泛型的关系

Java Type接口出现的原因以及它和泛型的关系

Java Type接口出现的原因以及它和泛型的关系

Java泛型很多人都用过,但是对于其中的原理可能很多人可能都不太清楚。

首先给出一个结论:Java的泛型是伪泛型,因为JVM在编译以后会将所有泛型参数擦除掉,这个就叫类型擦除。

下面用一段代码来证明,毕竟千言万语BB不如一句代码来的直接:

      List<String> list1 = new ArrayList<>();List<Integer> list2 = new ArrayList<>();System.out.println(list1.getClass() == list2.getClass());

结果出人意料:竟然是True。

经过JVM编译以后,两个List的class都返回了原始类型List,并没有带上各自的泛型参数。

为什么会这样?!

这是因为泛型是Java 1.5以后才出现的,所以以前的java代码根本就没有尖括号泛型这玩意,

为了保持兼容性,Java在运行时简单粗暴的把泛型信息给擦除掉了!

不得不说,Java的这种做法相当差劲,简直就是垃圾设计!

但是以上代码的泛型信息真的全部都消失了吗。

让我们反编译字节码文件看一下:

 javap -c -v TestTypeErace.class  // java反汇编命令

输出了一堆东西,但是重点看一下 如下信息:

 看见没有:虽然JVM擦除了对象的泛型参数,但是在编译阶段:泛型信息仍然保存到了java字节码文件中的LocalVariableTypeTable这张表里面。

所以Type接口这玩意儿就来了:Type接口可以通过反射获取到泛型类的泛型信息!

这就是Type接口出现的真正原因!

 以下是Type接口的层级结构图

不得不说Java这门破语言的概念是相当多,然后起的名字又是狗屁不通,不好理解!

简单说一下:Type接口作为所有类型的总接口,包括了原始类型和泛型,反正就是生搬硬套的搞出了这么一个东西,为了保证兼容性,向低版本兼容。

下面简单解释一下这几个子接口的作用:

Class: 原生类型的Class对象,也就是我们日常所有的类/接口,包括枚举,数组,注解等,但不包括基本类型:如int, float等。

Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个Class对象

当一个类/接口被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象==》 这里实际涉及到classLoader的累加器机制和它的源码部分。

备注:Java所有的类和接口被JVM装载以后,都会生成一个跟类相关的java.lang.Class对象(字节码文件),每个类型都会有一个Class对象,Class类的对象只能由JVM创建,无法通过new来创建,当类/接口被加载时,就会被JVM自动生成Class对象,这也是反射的基础。

剩下的几种全部针对泛型:

这里就不用英文释义了,因为命名实在太差劲了,什么叫参数化类型,完全扯淡!垃圾命名!

TypeVariable: 简单说就是泛型尖括号里面的类型比如Map<T,V>, TypeVariable指的就是T和V

ParameterizedType: 声明带有泛型的类型: 也就是带有尖括号的类型,比如List<T>, Map<T,V>,

只要带尖括号就是ParameterizedType,也就是泛型类型

GenericArrayType: 简单说就是泛型数组: 首先他是一个数组,然后数组的变量是泛型

就这么简单,举几个例子:

List<String> ,  T[],  Class<T>[] 这几个都是GenericArrayType

WildcardType: 通配符类型,这个不多说了,跟Class<?>差不多,实际用的很很少

这里重点讲讲ParameterizedType的两个重要Api:

getActualTypeArguments: 获取泛型参数列表:因为可能存在多个泛型,比如 SuperClass<T, V>,所以会返回 Type[] 数组

getRawType: 获取泛型尖括号前面的类型

总的来说Type的出现主要是为了配合泛型的反射操作,所以下面用一段代码简单证明一下:

public class BaseObj<T,U> {private List<T> items;private List<String> names;private BaseObj baseObj1;private BaseObj<T,U> baseObj2;private List list;private Map<T, U> map = new HashMap<>();private T t;private <E> T getItem(T t, E e) {return t;}public BaseObj<T,U> test(List<T> items, BaseObj<Integer,Integer> nums, T t) {return null;}public static void main(String[] args) {System.out.println("---------------输出泛型类尖括号里的类型----------------------------------------");// 用当前类名获取class对象无需getClass方法,只要类的实例才需要getClass方法Class<BaseObj> baseObjClass = BaseObj.class;TypeVariable<Class<BaseObj>>[] typeParameters = baseObjClass.getTypeParameters();for (TypeVariable<Class<BaseObj>> typeParameter : typeParameters) {System.out.println(typeParameter);}System.out.println("----------------输出泛型类属性中的泛型类型------------------------------------------------------");Field[] declaredFields = BaseObj.class.getDeclaredFields();for (Field field : declaredFields) {System.out.println(field.getType() + "=" + field.getName());Type genericType = field.getGenericType(); // 获取完整泛型:类名<泛型>if (genericType instanceof ParameterizedType) {ParameterizedType pType = (ParameterizedType) genericType;System.out.println("Typename :" + pType.getTypeName());  // 获取完整泛型:全路径类名<泛型>System.out.println("rawType :" + pType.getRawType()); // 获取泛型尖括号前面的类型System.out.println("ownerType :" + pType.getOwnerType()); // 获取父类类型// 获取泛型尖括号<>里面的实际数据类型,因为可能存在多个泛型,比如 SuperClass<T, V>,所以会返回 Type[] 数组Type[] types = pType.getActualTypeArguments();System.out.println("泛型参数列表: " + Arrays.asList(types));System.out.println("------------------------------------------");continue;}System.out.println("成员变量:"+field.getName() + "不是泛型变量!");System.out.println("genericType =>" + genericType);System.out.println("------------------------------------------");}}

可能你看的会有点懵逼,不要紧,你结合之前的文字说明,再结合代码注释就能慢慢看懂,因为我也是这么理解来的。