我试图说明类型擦除向某人解释它,但我发现了一些意想不到的东西。这是我试过的片段:
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
)?
答案 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)
,它会失败。
(这是堆污染是一个令人讨厌的错误的原因之一;失败可能发生在远离实际错误的代码的某个地方。)