Java 为什么在这种情况下允许抛出选中的异常类型?
我无意中注意到这个Java 为什么在这种情况下允许抛出选中的异常类型?,java,exception,language-lawyer,throw,checked-exceptions,Java,Exception,Language Lawyer,Throw,Checked Exceptions,我无意中注意到这个throw语句(从一些更复杂的代码中提取)编译: void foo() { try { } catch (Throwable t) { throw t; } } 有那么一个短暂但快乐的时刻,我以为我最终决定就这样死去了,但在这一点上,我仍然感到骄傲: void foo() { try { } catch (Throwable t) { Throwable t1 = t; throw t
throw
语句(从一些更复杂的代码中提取)编译:
void foo() {
try {
} catch (Throwable t) {
throw t;
}
}
有那么一个短暂但快乐的时刻,我以为我最终决定就这样死去了,但在这一点上,我仍然感到骄傲:
void foo() {
try {
} catch (Throwable t) {
Throwable t1 = t;
throw t1;
}
}
try
块不必为空;似乎只要代码不抛出已检查的异常,它就可以拥有代码。这似乎是合理的,但我的问题是,语言规范中的什么规则描述了这种行为?就我所见,显式禁止它,因为t
表达式的类型是一个已检查的异常,它没有被捕获或声明为抛出。(?)。下面是一个适用于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;
}
}
您可以阅读整篇文章来解释这些更改。JLS中详细描述了此行为,具体内容如下: 一种
throw
语句,其抛出的表达式是最终的或有效的
catch
子句C的最后一个异常参数可以引发异常
E类敌我识别码:
- E是一个异常类,
语句的try
块 声明C可以抛出;及try
- E的赋值是否与C的任何可捕获异常兼容 班级;及
- E与任何可捕获的异常都不兼容
在同一
try中,在C的左边声明的
子句的类 声明catch
您的第二个示例失败,因为
t1
不是“catch子句的异常参数”。我认为您提到的JLS-文本中的措辞是一个错误,应该用Java SE 7更新,而不是
描述预期行为的JLS文本位位于:
如果throw
语句的抛出表达式是catch
子句C的最终或有效的最终异常参数,则该语句可以抛出异常类E iff:
- E是声明C的
语句的try
块可以抛出的异常类;及try
- E的赋值与C的任何可捕获异常类兼容;及
- E的赋值与同一
语句中C左边声明的try
子句的任何可捕获异常类都不兼容catch
catch
-子句参数t
实际上是final(意味着它从未被赋值、递增或递减;请参见),throw t
只能抛出try
块可以抛出的东西
但是正如您所说的,§14.18中的编译时检查并没有考虑到这一点。§11.2.2不决定什么是允许的,什么是不允许的;相反,它应该是对各种限制的后果的分析。(该分析确实反馈到了规范的更规范的部分——§14.18本身在其第二个要点中使用了它——但§14.18不能仅仅说“如果它抛出了一个根据§11.2.2它不能抛出的异常,那就是编译时错误”,因为这是循环的。)
因此,我认为需要调整§14.18,以适应§11.2.2的意图
好发现 看起来是这样,但在langspec中它在哪里?14.18说“以下三个条件中至少有一个必须为真,否则会发生编译时错误”。这不是三个条件之一,确切地说。由于Java 7中引入的增强分析,可以确定第三个条件(throw语句包含在方法或构造函数声明中,并且表达式的类型可分配(§5.2)给声明的throws子句(§8.4.6,§8.8.5)中列出的至少一个类型。)是真的。但是表达式类型是
Throwable
,不能“分配”给RuntimeException
或Error
,因为这需要强制转换,看起来是§5.5,而不是§5.2。@Boann这是真的。但是,编译器现在可以检测到该可丢弃
必须是运行时异常
或错误
,因为可丢弃
的其他子类型不能在try
块中抛出。如果throwable可能是一个checked异常(因为这样的异常可能会在try
-块中抛出),那么它会抱怨抛出的正是这个checked异常(正如您在问题中已经提到的)。+1。这实际上提出了关于多批次的相同问题,catch(最终运行时异常|错误e){throw e;}
,因为根据§14.20,e
的类型是Throwable
。§14.18还说“尽管throw
语句可以抛出的异常类型在§11.2.2中指定”。这句话前面的要点有点措词不当。@ntoskrnl确实有点措词不当。我完全错过了11.2.2。与较旧的JLS相比,中的“三个条件”基本上没有变化,但发生了巨大的变化。有趣。但这肯定是正确的部分,因为如果我插入t=null代码>之前<代码>抛出t代码>,因此它不再是“有效的最终”,它现在抱怨未声明的异常。(接受你的答案,顺便说一句,因为它是最详细的。谢谢。)@ntoskrnl:对——但这并不能取代条件列表。(请注意,§11.2.2中的“可以扔”表示“可能扔”,而不是“允许扔”。)第三个条件不是“有点糟糕”