简体   繁体   English

java泛型类型擦除

[英]java generics type erasure

When declaring a class with type parameter <T extends A & B & C> ,type erasure process will replace T with type A . 当声明类型参数<T extends A & B & C>的类<T extends A & B & C> ,类型擦除过程将用类型A替换T But if variable of type T calls method declaring in interface B or C , what does Java do with it? 但是如果T类型的变量调用接口BC方法声明,Java会用它做什么?

And when we create an instance of generic type such as ArrayList<String> , the type parameter String will also be erased, but calling get method will return type String , where does this information come from since it has been erased ? 当我们创建一个泛型类型的实例,如ArrayList<String> ,类型参数String也将被删除,但是调用get方法将返回类型String ,这些信息来自哪里,因为它已被删除?

I know java reflection is used, but I need some specific explanation. 我知道使用java反射,但我需要一些具体的解释。

Reflection isn't used - casting is. 不使用反射 - 铸造是。 For example, take this code: 例如,请使用以下代码:

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();
    }
}

Now look at the bytecode for Generic (constructor removed): 现在看一下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    
}

Note the checkcast instructions before each of the invokeinterface calls to b() and c() . 请注意每个invokeinterface调用b()c()之前的checkcast指令。

The result is exactly as if Generic were actually written like this: 结果就像Generic实际上是这样编写的:

class Generic<T extends A> {
    T t;

    Generic(T t) {
        this.t = t;
    }

    void callMethods() {
        t.a();
        ((B) t).b();
        ((C) t).c();
    }
}

As for your question about ArrayList - the information about the return type of get() being the element type of the list is still stored as part of the ArrayList class. 至于你关于ArrayList的问题 - 关于作为列表元素类型的get()的返回类型的信息仍然存储为ArrayList类的一部分。 The compiler will again insert casts in the calling code, so: 编译器将再次在调用代码中插入强制转换,因此:

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

is equivalent at execution time to: 在执行时相当于:

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

One important aspect of this is that you can't ask an ArrayList object at execution time what T is - that information has been erased. 这个的一个重要方面是你不能在执行时询问一个ArrayList对象什么T - 该信息已被删除。

Type erasure effectively replaces all the generic type parameters with Object after the compiler has done its type checking. 在编译器完成类型检查后,类型擦除有效地用Object替换所有泛型类型参数。 In your <T extends A & B & C> example, A is erased just like everything else. 在你的<T extends A & B & C>示例中, A就像其他所有内容一样被删除。

When you call get on an ArrayList<String> , the compiler produces bytecode that casts the returned object to a string automatically. 当您在ArrayList<String>上调用get时,编译器会生成字节码,将返回的对象自动转换为字符串。 The list itself doesn't "know" that it's a list of strings (due to the type erasure), but the code that calls get knows that it expects the thing gotten from the list to be a string. 列表本身并不“知道”它是一个字符串列表(由于类型擦除),但是调用get的代码知道它希望从列表中get的东西是一个字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM