我知道这个问题之前已被多次询问过,但我正在寻找基于类型擦除的答案。
为什么编译器在向Integer
添加ArrayList<String>
时会出错?我希望通过add
中ArrayList
方法的类型擦除和字节代码来理解这一点。
答案 0 :(得分:7)
这与类型擦除或字节代码无关。编译器在删除泛型类型参数并生成字节代码之前会给出此错误。
向Integer
添加ArrayList<String>
时,编译器只会给您一个错误,因为Integer
不是String
的子类。
泛型只需在编译时添加一层类型安全。如果您使用原始ArrayList
代替ArrayList<String>
,则可以将String
和Integer
添加到ArrayList
。但是,无论您使用的是ArrayList
还是ArrayList<String>
,生成的字节代码都是相同的。
答案 1 :(得分:4)
即使类型擦除意味着在执行时间,VM也无法区分ArrayList<String>
和ArrayList<Integer>
(它们只是两个实例) of ArrayList
)编译器 知道类型参数,并知道什么是安全的。
所以如果你有:
ArrayList<Integer> integers = new ArrayList<Integer>();
integers.add("foo");
然后integers
的编译时类型为ArrayList<Integer>
,并且会检查在integers
上执行的所有操作。 ArrayList
中唯一add
次来电是
add(E e)
add(int index, E e)
现在没有从String
到Integer
的转换,因此这些重载都不适用于调用integers.add("foo")
,因此编译时错误。
基本上,泛型提供两件事:
Integer
引用,则编译器不会允许您添加String
引用关于强制转换的简单性 - 当类型参数为T
的类的方法返回类型T
的值且编译器知道T
是什么时,它可以将已擦除类型(通常为Object
)的适当强制转换插入到类型参数中。那是怎么回事:
List<Integer> integers = getListFromSomewhere();
Integer x = integers.get(0);
工作 - 编译器在调用代码中添加一个强制转换。 (在执行时仍会检查该演员表。)
答案 2 :(得分:0)
让我们从一个小例子开始(注意这会使用raw types;它们是邪恶的),
public static void main(String[] args) {
List<String> strings = Arrays.asList("Hello");
List<Integer> al = new ArrayList(strings);
for (Integer v : al) {
System.out.println(v);
}
}
它编译。如果我们运行它,我们得到
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at com.stackoverflow.Example.main(Example.java:11)
因为String
不是Integer
。我们可以用javap -v
35: invokeinterface #37, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
40: checkcast #43 // class java/lang/Integer
43: astore_3
44: getstatic #45 // Field java/lang/System.out:Ljava/io/PrintStream;
并且看到转换在编号为40的行上失败。通用类型用于编译时类型检查,在运行时(几乎)所有内容都是Object
。