在类型擦除过程中,Java编译器会擦除所有类型参数,如果type参数是有界的,则将每个参数替换为第一个绑定,如果type参数是无界的,则替换为Object。
但是当我们使用编译类引用方法时,编译器会确保在编译时进行类型检查。
有关。例如如果我在A类上使用泛型编译它然后通过类B引用它,在编译期间它将确保类型检查。
如果java在编译时擦除了类型,那么编译后的类文件如何确保类型检查?
答案 0 :(得分:12)
除了擦除的上限之外,在运行时没有类型检查。 Java Generics完全是关于编译器检查。
另一方面,如果类型参数信息从字节码中消失,那么您的问题可能只是编译器如何进行检查。答案就是它并没有作为一个整体从类文件中消失:它作为元数据附加,可供编译器(以及Reflection API)使用,但与执行代码无关。
答案 1 :(得分:2)
我不同意在运行时没有检查 - 但所有类型检查都是在编译时完成的。
查看生成的实际字节码总是很有趣。我们来看看这段代码
import java.util.*;
class Types{
public static void main(String [] args){
List<String> list = new ArrayList<String>();
list.add("hello");
System.out.println(list.get(0));
}
}
编译它,然后使用javap -c
对其进行反汇编,我们得到
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String hello
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_1
21: iconst_0
22: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
27: checkcast #8 // class java/lang/String
30: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
}
我们注意到两件事
checkcast
以确保从列表中检索的对象实际上是String
。这是编译器插入的运行时检查。在Generics之前手动引入的东西。