【问题标题】:java generics type erasurejava泛型类型擦除
【发布时间】:2011-11-01 06:51:10
【问题描述】:

当声明一个带有类型参数的类时 <T extends A & B & C>,类型擦除过程将用类型A 替换T。 但是如果T 类型的变量调用接口BC 中声明的方法,Java 会用它做什么?

而当我们创建ArrayList<String>等泛型类型的实例时,类型参数String也会被擦除,但调用get方法会返回类型 String,这个信息从哪里来?

我知道使用了java反射,但我需要一些具体的解释。

【问题讨论】:

    标签: java generics type-erasure


    【解决方案1】:

    不使用反射 - 使用强制转换。比如下面这段代码:

    interface A {
        void a();
    }
    
    interface B {
        void b();
    }
    
    interface C {
        void c();
    }
    
    class Generic<T extends A & B & C> {
        T t;
    
        Generic(T t) {
            this.t = t;
        }
    
        void callMethods() {
            t.a();
            t.b();
            t.c();
        }
    }
    

    现在查看Generic 的字节码(已删除构造函数):

    class Generic extends java.lang.Object{
    A t;
    
    void callMethods();
      Code:
       0:   aload_0
       1:   getfield        #2; //Field t:LA;
       4:   invokeinterface #3,  1; //InterfaceMethod A.a:()V
       9:   aload_0
       10:  getfield        #2; //Field t:LA;
       13:  checkcast       #4; //class B
       16:  invokeinterface #5,  1; //InterfaceMethod B.b:()V
       21:  aload_0
       22:  getfield        #2; //Field t:LA;
       25:  checkcast       #6; //class C
       28:  invokeinterface #7,  1; //InterfaceMethod C.c:()V
       33:  return    
    }
    

    注意invokeinterfaceb()c() 的每个调用之前的checkcast 指令。

    结果就好像Generic实际上是这样写的:

    class Generic<T extends A> {
        T t;
    
        Generic(T t) {
            this.t = t;
        }
    
        void callMethods() {
            t.a();
            ((B) t).b();
            ((C) t).c();
        }
    }
    

    至于您关于ArrayList 的问题-关于get() 作为列表元素类型的返回类型的信息仍存储为ArrayList 类的一部分。编译器将再次在调用代码中插入强制转换,所以:

    ArrayList<String> strings = new ArrayList<String>();
    strings.add("foo");
    String x = strings.get(0);
    

    在执行时等同于:

    ArrayList strings = new ArrayList();
    strings.add("foo");
    String x = (String) strings.get(0);
    

    其中一个重要方面是,您不能在执行时询问ArrayList 对象T 是什么 - 该信息已被删除。

    【讨论】:

    • 感谢您的回答。“get() 的返回类型是列表的元素类型的信息仍然存储为 ArrayList 类的一部分”。它在哪里存储这些信息?我使用一些软件扫描 ArrayList 类文件,但我找不到它。
    【解决方案2】:

    在编译器完成类型检查后,类型擦除有效地将所有泛型类型参数替换为Object。在您的 &lt;T extends A &amp; B &amp; C&gt; 示例中,A 就像其他所有内容一样被删除。

    当您在 ArrayList&lt;String&gt; 上调用 get 时,编译器会生成字节码,自动将返回的对象转换为字符串。列表本身并不“知道”它是一个字符串列表(由于类型擦除),但调用 get 的代码知道它期望从列表中获取的东西是一个字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 2017-09-24
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      相关资源
      最近更新 更多