为什么在这种情况下允许抛出检查的exception类型?

我偶然注意到这个throw语句(从一些更复杂的代码中提取)编译:

 void foo() { try { } catch (Throwable t) { throw t; } } 

对于一个短暂但快乐的时刻,我认为已经检查过的exception最终决定已经死了,但它仍然很高兴:

 void foo() { try { } catch (Throwable t) { Throwable t1 = t; throw t1; } } 

try块不必为空; 它似乎可以有代码,只要该代码不会抛出一个已检查的exception。 这似乎是合理的,但我的问题是,语言规范中的哪些规则描述了这种行为? 据我所知 , §14.18town语句明确禁止它,因为t表达式的类型是一个经过检查的exception,并且它没有被捕获或声明被抛出。 (?)

我认为§14.18中的措辞你提到的throw语句是JLS中的一个错误 – 应该用Java SE 7更新的文本,而不是。

描述预期行为的JLS文本位于第11.2.2节语句的exception分析中 :

throw语句表达式是catch子句C的最终或有效最终exception参数的throw语句可以抛出exception类E iff:

  • E是一个exception类,声明C的try语句的try块可以抛出; 和
  • E的赋值与C的任何可捕获exception类兼容; 和
  • E与同一try语句中在C左侧声明的catch子句的任何可捕获exception类不兼容。

第一个要点是相关要点; 因为catch -clause参数t实际上是final的(意味着它永远不会被赋值或递增或递减;参见§4.12.4final变量 ), throw t只能抛出try块可以抛出的东西。

但正如您所说,§14.18中的编译时检查不会对此作出任何限制。 §11.2.2不决定允许什么,不允许什么; 相反,它应该是对可以抛出的各种限制的后果的分析。 (这种分析确实反馈到规范的更规范的部分 – §14.18本身在其第二个子弹点中使用它 – 但§14.18不能只说“如果它抛出一个它不能的例外它是一个编译时错误抛出§11.2.2“,因为那将是循环的。)

所以我认为需要调整§14.18以适应§11.2.2的意图。

很好找!

这是因为Java 7中引入的Project Coin中包含的更改允许通过重新抛出原始exception来进行常规exception处理。 这是一个适用于Java 7但不适用于Java 6的示例:

 public static demoRethrow() throws IOException { try { throw new IOException("Error"); } catch(Exception exception) { /* * Do some handling and then rethrow. */ throw exception; } } 

您可以在此处阅读解释更改的整篇文章。

在11.2中的JLS中详细描述了此行为。 编译时检查例外 :

throw语句表达式是catch子句C的最终或有效最终exception参数的throw语句可以抛出exception类E iff:

  • E是一个exception类,声明C的try语句的try块可以抛出 ; 和

  • E的赋值与C的任何可捕获exception类兼容; 和

  • E与同一try语句中在C左侧声明的catch子句的任何可捕获exception类不兼容。

(强调我的。)

您的第二个示例失败,因为t1不是“ catch子句的exception参数”。