欺骗类型擦除的集合

时间:2015-01-18 01:40:06

标签: java casting type-erasure

我试图说明类型擦除向某人解释它,但我发现了一些意想不到的东西。这是我试过的片段:

Object o = new ArrayList<Integer>();
List<String> list = (List<String>) o;
list.add("Oups");
System.out.println(list);

我曾预料到会在最后一行抛出ClassCastException(或类似的东西),但事实并非如此。我还尝试在转换为ArrayList之前使用整数值填充Object,结果是相同的。

为什么这与

不同
Integer[] arr0 = { 1,2,3,4 };
Object[] arr1 = arr0;
arr1[3] = "hey";

执行抛出异常(ArrayStoreException)?

3 个答案:

答案 0 :(得分:2)

(由于对问题的更改而更新。)

您的最后一行不会因为toString() ArrayList的实现根本不关心元素类型而引发异常。简单地说,它会在存储在其内部toString()元素数组中的每个对象上递归调用Object[],因此无需检查其元素的运行时类型。

但是,这段代码实际上也不会失败:

List<Integer> il = new ArrayList<Integer>();
Object o = il;
List<String> sl = (List<String>)o;
sl.add("Oups");
System.out.println(il.get(0));

这是因为System.out.println的参数类型是Object,因此编译器会在Integer处优化隐式强制转换为get(),因为它是System.out.println(il.get(0) + 5); 不严格&#34;必要&#34;。有人可能会或许不会认为这对编译器来说是不好的,因为早期的演员阵容会更早地发现错误,但事实就是如此。

另一方面,用这个替换最后一行会抛出异常:

Integer

因为现在您正在执行一个假定元素为{{1}}的操作,并且演员阵容包含在生成的代码中。

另一方面,

原始数组具有元素类型的运行时知识,并会在每次插入时断言。

答案 1 :(得分:1)

由于在运行时类型删除它的类型Object无论如何都是List是空的,你永远不会迭代它

List<Integer> o = new ArrayList<Integer>();
List<String> list = (List<String>) (Object) o;
list.add("Oups");
for (Integer i : o) { // <-- Iterate the List<Integer>
    System.out.println(i);
}

将抛出(在运行时)

Exception in thread "main" java.lang.ClassCastException: 
    java.lang.String cannot be cast to java.lang.Integer

答案 2 :(得分:1)

ArrayList内的数组是Object[],而不是Integer,因此向其分配任意对象都没有问题。当你尝试执行list.get(0)时它会中断,因为编译器会插入一个隐式的强制转换,(Integer) list.get(0),它会失败。

(这是堆污染是一个令人讨厌的错误的原因之一;失败可能发生在远离实际错误的代码的某个地方。)