【发布时间】:2016-10-21 22:28:00
【问题描述】:
考虑以下代码:
static void main(String[] args) {
try {
} catch (Exception e) {
throw e;
}
}
This code compiles 无需在方法签名中添加throws Exception。 (用Throwable 代替Exception 的行为也类似)。
我明白为什么它可以安全运行,因为Exception 实际上不能在try 块中抛出,因此不能抛出已检查异常;我很想知道这种行为是在哪里指定的。
这不仅仅是因为永远无法到达throw e:以下代码也可以编译:
static void stillCompilesWithThrownUncheckedException() {
try {
throw new NullPointerException();
} catch (Exception e) {
throw e;
}
}
但是如果你抛出一个已检查的异常,它不会像我预期的那样编译:
static void doesNotCompileWithThrownCheckedException() {
try {
throw new Exception();
} catch (Exception e) {
throw e; // error: unreported exception Exception; must be caught or declared to be thrown
}
}
在JLS Sec 11.2.2 中,它说:
throw语句(第 14.18 节)其抛出的表达式具有静态类型 E,并且不是 final 或有效的 final 异常参数,可以抛出 E 或抛出的表达式可以抛出的任何异常类。
我对这句话的解释是throw e可以抛出Exception,因为e的静态类型是Exception。然后,在JLS Sec 11.2.3:
如果一个方法或构造函数体可以抛出某个异常类 E,而 E 是一个已检查的异常类并且 E 不是该方法或构造函数的 throws 子句中声明的某个类的子类,则这是一个编译时错误。
但这不是前两种情况下的编译时错误。语言规范中在哪里描述了这种行为?
编辑:在将其标记为骗子后,我将提出后续问题:为什么在第一个示例中throw e; 不被视为不可访问。
在JLS Sec 14.21 中更容易找到答案:
如果满足以下两个条件,则可以访问 catch 块 C:
C 的参数类型要么是未经检查的异常类型,要么是 Exception,要么是 Exception 的超类,或者 try 块中的某些表达式或 throw 语句是可访问的,并且可以抛出类型可分配给 C 参数类型的检查异常. (如果包含它的最里面的语句是可访问的,则表达式是可访问的。)
有关表达式的正常和突然完成,请参阅 §15.6。
try语句中没有更早的catch块A,使得C的参数类型与A的参数类型相同或者是A的子类。
这两个都是真的(它的类型是Exception,并且没有更早的catch块),所以它是“可达的”。我猜想将空的 try 块作为特例调用对于这种勉强有用的构造来说太大了。
【问题讨论】:
-
当我尝试用空的
try块编译main方法时,我收到错误incompatible types: Exception cannot be converted to Throwable。您使用的是什么版本的 Java? -
@EliSadoff 演示在 Ideone 上,所以无论他们使用什么。我想是sun-jdk-8u51。
-
@Tunaki 相关,但这只是描述现象,并没有实际给出 JLS 的具体指针。
-
"Why is throwing a checked exception type allowed in this case?" 对此进行了介绍,但我不会把它当作一个骗子来关闭,因为它是针对 Java 7 的,而 Java 8 中的细节可能已经改变。
标签: java