首页 文章

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

提问于
浏览
16

我偶然注意到这个 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 回答

  • 13

    11.2. Compile-Time Checking of Exceptions中的JLS中详细描述了此行为:

    throw语句,其抛出的表达式是catch子句C的最终或有效最终异常参数,可以抛出异常类E iff:E是一个异常类,声明C可以抛出的try语句的try块;和E的任务兼容C的可捕获异常类;和E不是在同一个try语句中与C左边声明的catch子句的任何可捕获异常类兼容的赋值 .

    (强调我的 . )

    您的第二个示例失败,因为 t1 不是“ catch 子句的异常参数” .

  • 3

    我认为你提到的§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不是在同一个try语句中与C左边声明的catch子句的任何可捕获异常类兼容的赋值 .

    第一个要点是相关要点;因为 catch -clause参数 t 实际上是最终的(意味着它永远不会被赋值或递增或递减;请参阅§4.12.4 final Variables), throw t 只能抛出 try 块可能抛出的内容 .

    但正如您所说,§14.18中的编译时检查不会对此作出任何限制 . §11.2.2不决定允许什么,不允许什么;相反,它应该是对可以抛出的各种限制的后果的分析 . (这种分析确实反馈到规范的更规范的部分 - §14.18本身在其第二个子弹点中使用它 - 但§14.18不能只说“如果它抛出一个它不能的例外它是一个编译时错误抛出§11.2.2“,因为那将是循环的 . )

    所以我认为需要调整§14.18以适应§11.2.2的意图 .

    很好找!

  • 8

    这是因为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 .

相关问题