繁体   English   中英

为什么第4行不生成未经检查的异常?

[英]Why doesn't line 4 generate an unchecked exception?

/*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>的类型参数。)

编译器认为 ls将真正引用字符串列表 - 如果尚未进行危险操作,则第4行将是“安全的”(就类型安全而言) - 即第2行。

如果不涉及其他警告, List<String>应始终引用仅包含对字符串(或null)的引用的列表。 但是如果你已经打破了类型安全性,那么所有的赌注都是关闭的(除了VM将在执行时捕获那些类型的违规)。

警告显示哪条线是危险的 - 在这种情况下,你正在使用原始类型的地方,即你说,“好吧,我们这里有一个清单。我不知道它里面有什么。”

4号线不是指原始类型的任何变量-它是指调用getList<String> ,并指派返回值的String变量。 如果您认为这应该产生警告,请显示Java语言规范的哪一部分建议警告是适当的 - 我认为您会发现很难这样做。

第4行不产生编译时警告,因为ls的类型是List<String> ,这意味着它的get方法返回Stringnull 任何违规都将在编译时捕获并导致ClassCastException

编辑:

编译器知道有许多事情,字节码验证器和解释器没有。

  1. 通用类型
  2. 仅编译时注释
  3. 检查异常
  4. 外层私人

java编译器知道这些事情并因此指出它们的使用中的错误,但是字节码验证器和解释器不这样做,因此它在生成用于解释器的.class文件部分时“擦除”它们。

所以编译的步骤是

  1. 通过查找.java.class文件来查找所需的所有类。
  2. 确保所有类型检查。
  3. 擦除解释器不需要的信息(但是如果在其输入CLASSPATH上使用.class调用javac,则将其隐藏起来)。
  4. 生成输出.class文件。

第4行没有给出警告,因为ls被声明为List<String>ls.get(0)返回一个String(就编译器所知),并且你将它分配给一个String变量s

对于编译器来说,一切看起来都是正确的 - 它没有看到你“非法”将它指向List<Integer>的事实。

但是,您将获得ClassCastException 运行时

第4行绝对没问题。 您可能认为您的代码是:

String s = l.get(0);   // this will not compile without the cast

第4行不保证未经检查的警告,因为它不会引入类型错误的可能性。 ls的声明表示它是List<String> ,而List<String>包含String 它不是一种称为堆污染的病态情况,只有在未经检查的警告被错误地抑制时才会出现,但这不是第4行的错误。

但是,第4 ClassCastException运行时抛出ClassCastException ,因为编译器知道堆污染的可能性,并在调用其上的方法之前发出额外的强制转换来验证返回的对象是否是正确的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM