• 浅谈泛型擦除


    泛型擦除

      在编码阶段使用泛型时加上的类型参数,会被编译器在编译阶段去掉,这个过程叫做泛型擦除。

      泛型主要用于编译阶段。在编译后生成的Java字节码文件中不包含泛型中的类型信息。例如,在编码时定义的ListList经过编译后统一为List。JVM读取的只是List,由泛型附加的类型信息对JVM来说是不可见的。

    Java核心技术卷I解释:

      无论何时定义一个泛型类型,都会自动提供一个相应的原始类型(raw type)。这个原始类型的名字就是去掉类型参数后的泛型类型名。类型变量会被擦除(erased),并替换为其限定类型(或者,对于无限定的变量则替换为Object)。

    例如,

    泛型类Pair如下:

    public class Pair<T>{
      private T first;
      private T second;
      public Pair(T first,T second){
        this.first=first;
        this.second=second;
      }
      public T getFirst(){return first;}
      public T getSecond(){return second;}
      public void setFirst(T newValue){first=newValue;}
      public void setSecond(T newValue){second=newValue;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Pair的原始类型如下所示:

    public class Pair{
      private Object first;
      private Object second;
      public Pair(Object first,Object second){
        this.first=first;
        this.second=second;
      }
      public Object getFirst(){return first;}
      public Object getSecond(){return second;}
      public void setFirst(Object newValue){first=newValue;}
      public void setSecond(Object newValue){second=newValue;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      因为T是一个无限定的变量,所以直接用Object替换。

      在程序中可以包含不同类型的Pair,例如PairPair。不过擦除类型后,它们都会编程原始的Pair类型。

      假定我们声明了泛型上限:

    public class Interval<T extends Comparable&Serializable> implements Serializable{
      private T lower;
      private T upper;
      ...
      public Interval(T first,T second){
        if(first.compareTo(second)<=0){lower=first;upper=second;}
        else{lower=second;upper=first;}
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      此时原始类型如下所示:

    public class Interval implements Serializable{
      private Comparable lower;
      private Comparable upper;
      ...
      public Interval(Comparable first,Comparable second){
        if(first.compareTo(second)<=0){lower=first;upper=second;}
        else{lower=second;upper=first;}
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      如果将限定切换为class Interval,原始类型会用Serializable替换T,而编译器在必要时要向Comparable插入强制类型转换。

    (1)转换泛型表达式

      编写一个泛型方法调用时,如果擦出了返回类型,编译器会插入强制类型转换。例如:

    Pair<Employee> buddies=...;
    Employee buddy=buddies.getFirst();
    
    • 1
    • 2

      getFirst擦除类型后的返回类型是Object。编译器自动插入转换到Employee的强制类型转换。也就是说,编译器把这个方法调用转换为两条虚拟机指令:

    • 对原始方法Pair.getFirst的调用。
    • 将返回的Object类型强制转换为Employee类型。

      当访问一个泛型字段时,也要插入强制类型转换。假设Pair类的first字段和second字段都是公共的。表达式

    Employee buddy=buddies.first;
    
    • 1

      也会在结果字节码中插入强制类型转换。

    (2)转换泛型方法

      类型擦除也会出现在泛型方法中。

    public static <T extends Comparable> T min(T[] a)
    
    • 1

      经过泛型擦除后,只剩下:

    public static Comparable min(Comparable[] a);
    
    • 1

      注意,类型参数T已经被擦出了,只留下了限定类型Comparable。

    泛型擦除带来的问题

      当然,泛型擦除也带来了许多问题,这里就不细讲了,只是小总结下:

    • 不能用基本类型实例化类型参数
    • 运行时类型查询只适用于原始类型
    • 不能创建参数化类型的数组
    • 不能实例化类型变量
    • 不能构造泛型数组
    • 泛型类的静态上下文中类型变量无效
    • 不能抛出或捕获泛型类的实例
    • 可以取消对检查型异常的检查
    • 注意擦除后的冲突

    参考:
    《剑指Java》
    《Java核心技术卷I》
    泛型详解
    泛型之类型擦除

  • 相关阅读:
    第十四届蓝桥杯校模拟赛-编程大题详解+代码(二)
    Springcloud(一):springcloud使用nacos作为注册中心和配置中心
    好用的工具推荐
    远程管理通道安全SSH协议主机验证过程
    go 线程限制数量 --chatGPT
    un8.1:在nacos微服务已经被注册user-center和content-center
    SpringBoot集成MyBatis
    doc转html后添加style和导航
    嵌入式系统开发笔记91:认识ARM微控制器架构
    python基于PHP+MySQL的药店药品进销存管理系统
  • 原文地址:https://blog.csdn.net/qq_43753724/article/details/133532366