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

时间:2014-07-27 14:01:51

标签: 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 t1;
    }
}

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

3 个答案:

答案 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子句的"异常参数"。