为什么在以下情况下throw e
允许,但不允许?throw a
void test() {
try {
System.out.println();
} catch (Error | RuntimeException e) {
var a = e;
//throw a; unreported exception Throwable; must be caught or declared to be thrown
throw e;
}
}
所有这些直观上都是有道理的,但从JLS 14.20
多 catch 子句可以看作是一系列单 catch 子句。也就是说,异常参数类型表示为联合的 catch 子句等同
D1|D2|...|Dn
于一系列n 个catch 子句,其中异常参数的类型分别为类类型。在每个n 个D1, D2, ..., Dn
catch 子句的 Block 中,异常参数的声明类型为。lub(D1, D2, ..., Dn)
因为lub(Error,RuntimeException)
是Throwable
,上面的代码应该等同于:
try {
System.out.println();
} catch (Error e) {
Throwable lub = e;
throw lub;
} catch (RuntimeException e) {
Throwable lub = e;
throw lub;
}
(这显然不能编译)
此外, 的类型“当被视为未出现在赋值上下文中时”a
是 的类型( JLS 14.4.1),但如上所示,它与 的类型不同。e
e
我是否忽略了什么?
编辑:这不是为什么在某些情况下重新抛出 Throwable 而不声明它是合法的?的重复,因为这个问题特定于多重捕获(这里没有讨论),并且由于对 JLS 中解决多重捕获的特定片段的误解而出现。这个问题提供的答案帮助我把这些点联系起来:)
这在11.2.2中有描述。这基本上可以归结为“异常参数是
catch (...)
一种特殊情况”。我们的目标是展示
throw e;
编译。我们从 11.2.3 中的这一行开始:因此,我们想表明
try
语句test
不能抛出Throwable
。请注意,在这种情况下,“可以抛出”是一个严格定义的术语。根据 11.2.2,这里只与第二点相关。让我们来证明该
catch
块不能抛出Throwable
。也就是说,throw e;
不能抛出Throwable
。throw e;
这里第一点立即失败了,所以我们可以得出结论,throw e;
不能抛出Throwable
。如果是
throw a;
,则上述内容不适用,但另一个条款适用:Java 编译器足够智能,能够识别出捕获
e
和抛出e
将意味着抛出RuntimeException
或Error
,因此不是未声明的已检查异常。另一方面, 的类型
var a
被推断为Throwable
(而不是Error | RuntimeException
您可能认为的,因为这样的声明在该上下文中实际上无效!),因此throw a
意味着您抛出了Throwable
,因此编译器不能确定您没有抛出已检查的异常,因此它会引发错误。编译器是否可以识别这一点?很可能是的。这样做有意义吗?可能没有意义(或者至少这样做的价值可能不值得投资)。
这是根据14.18
throw
语句与11.2.2 语句的异常分析相结合所指定的规则得出的。根据 11.2.2 中的这条规则,具体如下
throw e
:另一方面,对于
throw a
,以下情况适用:JLS 的主要部分是11.2.2,其中规定:
这就解释了为什么以下会抛出
Error
或RuntimeException
:但以下情况则不同:
推断的类型
a
是Throwable
(上限)。如果封闭方法未声明为 throwing ,则会导致编译错误Throwable
。对于
var
声明,JLS 14.4.1规定如下:这很难解析,但我的理解是“T 的向上投影”与称为“最小上界”的类型相同。在本
test2
例中,它将是Throwable
。请注意,您在问题中引用的 JLS 文本是说明性的,而不是规范性的文本。它没有描述当您(重新)抛出捕获的异常时会发生什么。