Java 的泛型擦除

说到泛型,就不得说起“擦除”这个概念,相比于c#来说,java的泛型只存在于程序的源码中,在编译后的class文件中不存在,这个过程就是--泛型"擦除";所以,对于new ArrayList<String> 和 new ArrayList<Integer> 来说,两个对象在编译之后两者是一样的,通过反射均可以向集合中添加任意类型的对象;


编译前:

public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();

        System.out.println(c1 == c2);
}

编译后:

public static void main(String[] args) {
        new ArrayList();
        new ArrayList();
}

可以看到,在编译后的class文件中,ArrayList所声明的泛型不存在了;由此可知,如果想在程序运行期间获取泛型,看似是一件不可能完成的事情!

Map<Integer, Long> map = new HashMap<>();
map.put(0, 0L);
map.put(1, 1L);

String a = JSON.toJSONString(map);

map = JSON.parseObject(a, new TypeReference<Map<Integer, Long>>(){});

 

按照泛型擦除,泛型在编译后的class文件中就不存在了, 知会保留原始类型(Type的概念),那么TypeReference又是如何获取到具体的泛型类型的呢?  

查看TypeReference的源码,发现在其构造方法中,主要使用到了getGenericSuperclass()和getActualTypeArguments()两个方法,getGenericSuperclass()返回的是此对象带“泛型”的父类,而getActualTypeArguments()返回的是此父类中实际类型参数的Type 对象数组,说白了就是TypeReference<>中的泛型;

 
如果非匿名内部类,通过getActualTypeArguments() 获取的仍然是Type中的概念;如果声明了匿名内部类,则在编译时会将此匿名内部类但对生成一个class文件
 
创建的匿名内部类new TypeReference<Map<String,StudentEntity>>(){},在生成的的class文件中,编译器默认为TypeReference中的泛型Map<String,StudentEntity>原始类型,并不会进行擦除!类似于 我们实际创建一个类 Class TypeReference<Map<String,StudentEntity>>{} ,和Class Test<T>{}一样,在编译后Map、T依旧会存在!

由此,对于java泛型的“擦除”并不能一概而论,在运行期间,如果方法中出现带泛型的匿名内部类,那么泛型依旧会被保留下来,我们可以通过对应的方法获取到实际的泛型类型!
 

参考:https://www.jianshu.com/p/1f608fd05e20

参考:https://yq.aliyun.com/articles/257488

posted on 2019-11-04 20:04  wangsong412  阅读(386)  评论(0编辑  收藏  举报