我偶然注意到这个throw
语句(从一些更复杂的代码中提取)编译:
void foo() {
try {
} catch (Throwable t) {
throw t;
}
}
对于一个短暂但快乐的时刻,我认为已经检查过的异常最终决定已经死亡,但它仍然在这方面很高兴:
void foo() {
try {
} catch (Throwable t) {
Throwable t1 = t;
throw t1;
}
}
try
区块不必为空;它似乎只有代码不会抛出一个已检查的异常。这似乎是合理的,但我的问题是,语言规范中的哪些规则描述了这种行为?据我所知,§14.18 The throw Statement明确禁止它,因为t
表达式的类型是一个经过检查的异常,并且它没有被捕获或声明被抛出。 (?)
答案 0 :(得分:13)
这是因为Java 7中引入的Project Coin中包含的更改,允许通过重新抛出原始异常来进行常规异常处理。这是一个适用于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;
}
}
您可以阅读整篇文章,解释更改here。
答案 1 :(得分:8)
我认为你提到的§14.18 The throw
Statement中的措辞是JLS中的一个错误 - 应该用Java SE 7更新的文本,而不是。
描述预期行为的JLS文本位于§11.2.2 Exception Analysis of Statements:
throw
语句,其抛出的表达式是catch
子句C的最终或有效最终异常参数,可以抛出异常类E iff:
- E是一个异常类,声明C可以抛出的
try
语句的try
块;和- E的赋值与C的任何可捕获异常类兼容;和
- E与赋值在同一
catch
语句中的C左侧的try
子句的任何可捕获异常类不兼容。
第一个要点是相关要点;因为catch
- 子句参数t
实际上是最终的(意味着它从未被分配或递增或递减;请参阅§4.12.4 final
Variables),throw t
只能抛出try
阻止{1}}阻止。
但正如您所说,§14.18中的编译时检查不会对此作出任何限制。 §11.2.2不决定允许什么,不允许什么;相反,它应该是对可以抛出的各种限制的后果的分析。 (这种分析确实反馈到规范的更规范的部分 - §14.18本身在其第二个子弹点中使用它 - 但§14.18不能只说“如果它抛出一个它不能的例外它是编译时错误抛出§11.2.2“,因为这将是循环的。)
所以我认为需要调整§14.18以适应§11.2.2的意图。
很好找!
答案 2 :(得分:3)
11.2. Compile-Time Checking of Exceptions中的JLS详细描述了此行为:
一个
throw
语句,其抛出的表达式是最终的或有效的catch
子句C的最终异常参数可以抛出异常 E级iff:
E是
try
语句的try
块的异常类 声明C可以抛出;以及E的分配与C的任何可捕获异常兼容 班;以及
E与任何可捕捉异常的分配不兼容 同一
catch
中C语左侧声明的try
子句的类 言。
(强调我的。)
您的第二个示例失败,因为t1
不是catch
子句的"异常参数"。