将Integer添加到String的通用ArrayList时编译时错误

时间:2014-12-02 06:50:02

标签: java generics arraylist collections

我知道这个问题之前已被多次询问过,但我正在寻找基于类型擦除的答案。

为什么编译器在向Integer添加ArrayList<String>时会出错?我希望通过addArrayList方法的类型擦除和字节代码来理解这一点。

3 个答案:

答案 0 :(得分:7)

这与类型擦除或字节代码无关。编译器在删除泛型类型参数并生成字节代码之前会给出此错误。

Integer添加ArrayList<String>时,编译器只会给您一个错误,因为Integer不是String的子类。

泛型只需在编译时添加一层类型安全。如果您使用原始ArrayList代替ArrayList<String>,则可以将StringInteger添加到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)

现在没有从StringInteger的转换,因此这些重载都不适用于调用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