以下程序的输出是什么:
List<String> stringList = new ArrayList<>(); List<Integer> intList = new ArrayList<>();
//输出true System.out.println(stringList.class == intList.class);
输出为true,这意味两个list的class地址都一样,为同一个字节码文件。
这个试验也侧面反映出一个现象:泛型在运行时就不存在了。事实上也确实如此,那么是怎么做到在编译时能“限制”类型,而在运行时又没有了“限制”呢?
所谓的类型擦除(type erasure)
,指的是泛型只在编译时起作用,在进入JVM之前,泛型会被擦除掉,根据泛型定义的形式而被替换为相应的类型。这也说明了Java的泛型其实是伪泛型。
无界擦除
当泛型类型被声明为一个具体的泛型标识,或一个无界通配符
时,泛型类型将会被替代为Object
。
上界擦除
当泛型类型被声明为一个上界通配符
时,泛型类型将会被替代为相应上界的类型。如List<? extends Number>
,程序并不能够确定具体的类型,只知道是Number
或其子类,所以会擦除为Number
类型。
下界擦除
下界通配符
的擦除,同无界通配符
,只能确定下界类型,但是上界类型无法确定,所以只能替换为Object
。如List<? extends Integer>,程序并不能够确定具体的类型,只知道是
Number
或其子类,
所以会擦除为Object类型。
绕过编译时泛型类型检查
List<Integer> list = new ArrayList<>(); list.add(123); // 正常编译 list.add("string"); // 编译报错【不兼容的类型: java.lang.String无法转换为java.lang.Integer】
基于类型擦除,我们可以利用反射绕过这个限制。
List<Integer> list = new ArrayList<>(); list.add(123); try { // 由于List中的泛型参数没有设置上界,所以add方法可以add任何Object的子类型参数 Method method = list.getClass().getDeclaredMethod("add", Object.class); method.invoke(list, "string"); method.invoke(list, true); method.invoke(list, 45.6); } catch (Exception e) { e.printStackTrace(); }
标签:List,list,类型,add,擦除,泛型,原理 From: https://www.cnblogs.com/zhengbiyu/p/18238739