首页投稿(暂停使用,暂停投稿)程序员@IT·互联网

Java泛型之类型擦除

2017-12-05  本文已影响497人  程序熊大

本文首发于个人网站:Java阿杜

类型擦除

学过C++模板的,在使用Java泛型的时候,会感觉到有点不疑问,例如:(1)无法定义一个泛型数组、无法调用泛型参数对象中对应的方法(当然,通过extends关键字是可以做到,只是比较麻烦);(2)ArrayList<Integer>和ArrayList<String>在运行时的类型是相同的。Java中的泛型有这些问题,是它的实现机制决定的,即“类型擦除”。

  1. 类型擦除的定义:编译通过后,准备进入JVM运行时,就不再有类型参数的概念,换句话说:每定义一个泛型类型,JVM会自动提供一个对应的原生类;

    public class Holder4<T> {
    
        private T a;
        private T b;
        private T c;
    
        public Holder4(T a, T b, T c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        public T getA() {
            return a;
        }
    
        public T getB() {
            return b;
        }
    
        public T getC() {
            return c;
        }
    
        public void setA(T a) {
            this.a = a;
        }
    
        public void setB(T b) {
            this.b = b;
        }
    
        public void setC(T c) {
            this.c = c;
        }
    
    
     public static void main(String[] args) {
         Holder4<Automobile> holder4 = new Holder4<>(new Automobile(),new Automobile(), new Automobile());
    
         Automobile a = holder4.getA(); //编译器帮忙转型,不需要显式转型
         Automobile b = holder4.getB();
         Automobile c = holder4.getC();
     }
    }
    

    在Java中,每定义一个泛型类型,就会自动提供一个对应的原始类型,例如:

    public class Holder4Raw {
    
        private Object a;
        private Object b;
        private Object c;
    
        public Holder4Raw(Object a, Object b, Object c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        public Object getA() {
            return a;
        }
    
        public Object getB() {
            return b;
        }
    
        public Object getC() {
            return c;
        }
    
        public void setA(Object a) {
            this.a = a;
        }
    
        public void setB(Object b) {
            this.b = b;
        }
    
        public void setC(Object c) {
            this.c = c;
        }
    
        public static void main(String[] args) {
            Holder4Raw holder4Raw = new Holder4Raw(new Automobile(),new Automobile(), new Automobile());
    
            Automobile a = (Automobile) holder4Raw.getA();  //显示的转型
            Automobile b = (Automobile) holder4Raw.getB();
            Automobile c = (Automobile) holder4Raw.getC();
        }
    }
    
  2. 为什么选择这种实现机制?

    • 在Java诞生10年后,才想实现类似于C++模板的概念,即泛型;
    • Java的类库是Java生态中非常宝贵的财富,必须保证向后兼容(即现有的代码和类文件依旧合法)和迁移兼容(泛化的代码和非泛化的代码可互相调用)基于上面这两个背景和考虑,Java设计者采取了“类型擦除”这种折中的实现方式。
  3. Java泛型依赖编译器实现,只存在于编译期,JVM中没有泛型的概念;那么,编译器做了什么工作呢?(1)set方法是编译期检查;(2)get方法的返回值进行转型,编译器插入了一个checkcast语句。

    我们通过字节码进行观察,可以看出:(1)Holder4和Holder4Raw两个类的字节码完全相同;(2)在main函数的33、41和49行就是编译器插入的checkcast语句;

    public class org.java.learn.generics.Holder4<T> {
      public org.java.learn.generics.Holder4(T, T, T);
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: aload_0
           5: aload_1
           6: putfield      #2                  // Field a:Ljava/lang/Object;
           9: aload_0
          10: aload_2
          11: putfield      #3                  // Field b:Ljava/lang/Object;
          14: aload_0
          15: aload_3
          16: putfield      #4                  // Field c:Ljava/lang/Object;
          19: return
    
      public T getA();
        Code:
           0: aload_0
           1: getfield      #2                  // Field a:Ljava/lang/Object;
           4: areturn
    
      public T getB();
        Code:
           0: aload_0
           1: getfield      #3                  // Field b:Ljava/lang/Object;
           4: areturn
    
      public T getC();
        Code:
           0: aload_0
           1: getfield      #4                  // Field c:Ljava/lang/Object;
           4: areturn
    
      public void setA(T);
        Code:
           0: aload_0
           1: aload_1
           2: putfield      #2                  // Field a:Ljava/lang/Object;
           5: return
    
      public void setB(T);
        Code:
           0: aload_0
           1: aload_1
           2: putfield      #3                  // Field b:Ljava/lang/Object;
           5: return
    
      public void setC(T);
        Code:
           0: aload_0
           1: aload_1
           2: putfield      #4                  // Field c:Ljava/lang/Object;
           5: return
    
      public static void main(java.lang.String[]);
        Code:
           0: new           #5                  // class org/java/learn/generics/Holder4
           3: dup
           4: new           #6                  // class org/java/learn/generics/Automobile
           7: dup
           8: invokespecial #7                  // Method org/java/learn/generics/Automobile."<init>":()V
          11: new           #6                  // class org/java/learn/generics/Automobile
          14: dup
          15: invokespecial #7                  // Method org/java/learn/generics/Automobile."<init>":()V
          18: new           #6                  // class org/java/learn/generics/Automobile
          21: dup
          22: invokespecial #7                  // Method org/java/learn/generics/Automobile."<init>":()V
          25: invokespecial #8                  // Method "<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
          28: astore_1
          29: aload_1
          30: invokevirtual #9                  // Method getA:()Ljava/lang/Object;
          33: checkcast     #6                  // class org/java/learn/generics/Automobile,get方法的转型
          36: astore_2
          37: aload_1
          38: invokevirtual #10                 // Method getB:()Ljava/lang/Object;
          41: checkcast     #6                  // class org/java/learn/generics/Automobile,get方法的转型
          44: astore_3
          45: aload_1
          46: invokevirtual #11                 // Method getC:()Ljava/lang/Object;
          49: checkcast     #6                  // class org/java/learn/generics/Automobile,get方法的转型
          52: astore        4
          54: return
    }
    

参考资料

  1. 《Java编程思想》
  2. 《Effective Java》
  3. 《Java核心技术》
上一篇下一篇

猜你喜欢

热点阅读