java泛型类型擦除

时间:2011-11-01 06:51:11

标签: java generics type-erasure

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

当我们创建一个泛型类型的实例,例如ArrayList<String>时,类型参数String也将被删除,但调用get方法将返回类型 String,此信息来自哪里,因为它已被删除?

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

2 个答案:

答案 0 :(得分:6)

不使用反射 - 投射是。例如,请使用以下代码:

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    
}

b()c()的每次checkcast来电之前,请注意invokeinterface条说明。

结果就像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是什么 - 该信息已被删除。

答案 1 :(得分:2)

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

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