在声明带有类型参数<T extends A & B & C>的类时,类型擦除过程将用类型A替换T。但是,如果T类型的变量调用接口B或C中的方法声明,那么它会做什么呢?
当我们创建一个泛型类型的实例时,比如ArrayList<String>,类型参数String也会被擦除,但是调用get方法会返回类型String,既然它已经被擦除了,那么这个信息从哪里来呢?
我知道使用了java反射,但我需要一些具体的解释。
发布于 2011-11-01 15:04:13
不使用反射,而是使用强制转换。例如,以下面的代码为例:
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
}注意每个invokeinterface调用b()和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是什么--该信息已被擦除。
发布于 2011-11-01 15:06:50
在编译器完成类型检查之后,类型擦除可以有效地将所有泛型类型参数替换为Object。在您的<T extends A & B & C>示例中,A被擦除,就像其他所有东西一样。
在ArrayList<String>上调用get时,编译器会生成自动将返回的对象转换为字符串的字节码。列表本身并不“知道”它是一个字符串列表(由于类型擦除),但是调用get的代码知道它期望从列表中获得的东西是一个字符串。
https://stackoverflow.com/questions/7963324
复制相似问题