/*1.*/ List l = new ArrayList<Number>();
/*2.*/ List<String> ls = l; // unchecked warning
/*3.*/ l.add(0, new Integer(42)); // another unchecked warning
/*4.*/ String s = ls.get(0);
如果第2行和第3行产生未经检查的警告,那么为什么第4行不会生成未经检查的警告,因为编译器不知道'ls'指的是什么(List<String>
或List<Integer>
)
(注意:从OP的原始帖子进行编辑,以使代码显示为可能的意图 - 特别是在任何地方都包含List<E>
的类型参数。)
答案 0 :(得分:8)
编译器认为 ls
将真正引用字符串列表 - 如果没有危险,第4行将是“安全的”(就类型安全而言)运作 - 即第2行。
如果不涉及其他警告,List<String>
应始终引用仅包含对字符串的引用(或null)的列表。但是如果你已经打破了类型安全性,那么所有的赌注都会被取消(除了VM会在执行时捕获那些类型的违规行为)。
警告显示哪条线是危险的 - 在这种情况下,你正在使用原始类型的地方,即你说,“好吧,我们这里有一个清单。我不知道它里面有什么虽然“。
第4行不引用任何原始类型的变量 - 仅指的是在get
上调用List<String>
,并将返回值分配给{{ 1}}变量。如果您认为这应该产生警告,请显示Java语言规范的哪一部分建议警告是适当的 - 我认为您会发现很难这样做。
答案 1 :(得分:3)
第4行不产生编译时警告,因为ls
的类型为List<String>
,这意味着其get
方法返回String
或null
。任何违规都将在编译时捕获,并将导致ClassCastException
。
编辑:
编译器知道字节码验证器和解释器没有很多东西。
java编译器知道这些事情并因此指出它们的使用中的错误,但是字节码验证器和解释器不会在生成用于解释器的.class
文件部分时“擦除”它们。
所以编译中的步骤是
.java
和.class
文件来查找所需的所有类。.class
调用javac,则将其隐藏起来。).class
个文件。答案 2 :(得分:1)
第4行没有发出警告,因为ls
被声明为List<String>
,ls.get(0)
返回一个字符串(就编译器所知),并且您正在分配它是一个String变量s
对于编译器,一切看起来都是正确的 - 它没有看到你“非法”将其指向List<Integer>
的事实。
但是,您将获得ClassCastException 运行时。
答案 3 :(得分:0)
第4行绝对没问题。您可能认为您的代码是:
String s = l.get(0); // this will not compile without the cast
答案 4 :(得分:0)
第4行不保证未经检查的警告,因为它不会引入类型错误的可能性。 ls
的声明表示它是List<String>
,List<String>
包含String
。它不是一种称为heap pollution的病态情况,只有在未经检查的警告被错误地抑制时才会出现,但这不是第4行的错误。
然而,第4行在运行时抛出ClassCastException
,因为编译器知道堆污染的可能性,并发出额外的强制转换来验证返回的对象是在调用方法之前的正确类型。