随笔 - 171  文章 - 0  评论 - 0  阅读 - 62245

泛型擦除的原理

以下程序的输出是什么:

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();
}
复制代码

 

 

 

posted on   zhengbiyu  阅读(27)  评论(0编辑  收藏  举报

点击右上角即可分享
微信分享提示