zoukankan      html  css  js  c++  java
  • 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();
           }
       }
    
    1. 为什么选择这种实现机制?
    • 在Java诞生10年后,才想实现类似于C++模板的概念,即泛型;
    • Java的类库是Java生态中非常宝贵的财富,必须保证向后兼容(即现有的代码和类文件依旧合法)和迁移兼容(泛化的代码和非泛化的代码可互相调用)基于上面这两个背景和考虑,Java设计者采取了“类型擦除”这种折中的实现方式。
    1. 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核心技术》
    原文作者:duqicauc
    转载地址:Java泛型之类型擦除 | Spring For All
    from: https://zhuanlan.zhihu.com/p/31741402
  • 相关阅读:
    iOS-基础控件(UILabel,UITextField,UIButton,UIImageView)属性
    iOS-基础控件-UIView(bounds和frame的区别)
    iOS-Senior21-环信(代码)
    iOS-Senior21-环信
    iOS-Senior20-Map地图
    iOS-Senior20-Map定位
    UI进阶 SQLite错误码
    UI进阶 动画
    第三方类AFNetworking
    UI进阶 CocoaPods的安装使用步骤
  • 原文地址:https://www.cnblogs.com/GarfieldEr007/p/10197906.html
Copyright © 2011-2022 走看看