为什么try..finally阻止不将原始异常注册为抑制?

dou*_*lep 32 java exception-handling try-finally

使用以下代码:

try {
    throw new RuntimeException ("main");
}
finally {
    throw new RuntimeException ("finally");
}
Run Code Online (Sandbox Code Playgroud)

我得到这个结果:

Exception in thread "main" java.lang.RuntimeException: finally
        at test.main(test.java:12)
Run Code Online (Sandbox Code Playgroud)

但是,通过在Java 7中添加抑制的异常,当finally块本身因异常而失败时,语言将原始"主"异常注册为抑制是不合逻辑的?目前我必须手动模拟这个:

try {
    throw new RuntimeException ("main");
}
catch (RuntimeException exception) {
    try {
        throw new RuntimeException ("finally");
    }
    catch (RuntimeException exception2) {
        exception2.addSuppressed (exception);
        throw exception2;
    }
}
Run Code Online (Sandbox Code Playgroud)

获得更多有用的(用于了解正在发生的事情)结果:

Exception in thread "main" java.lang.RuntimeException: finally
        at test.main(test.java:13)
        Suppressed: java.lang.RuntimeException: main
                at test.main(test.java:9)
Run Code Online (Sandbox Code Playgroud)

编辑:澄清我在想什么.目前的Java版本是8,抑制异常不是一个全新的功能.但try..finally仍然没有包含它们.有什么东西阻止这种情况发生吗?

Ran*_*niz 8

因为try-with-resources是语法糖,而Java编译器不会以相同的方式扩展常规的try-finally块.

看看下面的代码:

try(FileInputStream fstream = new FileInputStream("test")) {
    fstream.read();
}
Run Code Online (Sandbox Code Playgroud)

编译然后反编译(使用IntelliJ IDEA)时,它看起来像这样:

FileInputStream fstream = new FileInputStream("test");
Throwable var2 = null;

try {
    fstream.read();
} catch (Throwable var19) {
    var2 = var19;
    throw var19;
} finally {
    if(fstream != null) {
        if(var2 != null) {
            try {
                fstream.close();
            } catch (Throwable var17) {
                var2.addSuppressed(var17);
            }
        } else {
            fstream.close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

而这段代码:

FileInputStream fstream = new FileInputStream("test");
try {
    fstream.read();
} finally {
    fstream.close();
}
Run Code Online (Sandbox Code Playgroud)

在编译和反编译时看起来完全相同.

现在,可以肯定的是,所有finally区块都应该以与上面相同的方式进行扩展,但出于某种原因,这些区块要么被忽视,要么被反对.

我建议你打开一个功能请求,因为我认为这是一个明智的功能.

  • 我把这个问题读作"*为什么Java编译器不会以相同的方式扩展常规的try-finally块*",尽管我认为这个答案仍然增加了一些有用的东西. (4认同)

biz*_*lop 5

这不是一个权威的答案,但它看起来像这样的改变要么破坏兼容性,或try-with-resourcestry-finally将与相互矛盾.

语义是传播try-with-resourcestry块抛出的异常,在调用close()注册为suppress 的方法时抛出异常.从实际的角度来看,这是有道理的,你想要抓住你的"真实"异常,然后如果你愿意,也可以处理没有关闭的资源.

但是在try-finally它中被抛出的异常被finally传播(而一个try被"吞噬"),因此我们可以选择三种不好的解决方案:

  1. 颠倒逻辑,try-finally使其try-with-resources与所有以前的代码兼容并破坏代码(或者更确切地说,bug)兼容性.
  2. 继续传播" close()"异常,并将try异常注册为suppress,使两个构造彼此不一致.
  3. 只要保持原样.

主观上我认为3比1或2差,但是否则很容易争论.但我怀疑这是语言开发人员所面临的两难选择,他们碰巧选择了选项3.

  • ...例如你可以说1是最好的选择,因为`try-finally`在概念上是破坏的并且修复它(并且使它与`try-with-resources`一致比为了保持向后兼容性更重要无论如何可能有错误的代码. (2认同)
  • 短语"在`finally`中被抛出的异常被注册为被压制"是不正确的.try-with-resource构造会将`close()`方法*中抛出的异常注册为抑制,但是如果你添加一个显式的`finally`块,抛出的异常仍然会隐藏`try`块中抛出的任何异常或`close()`操作.值得注意的是,这种行为符合其他可疑行为,即`finally`中的任何控制流语句,即`break`,`continue`或`return`,将隐藏在`try`块中抛出的任何异常. (2认同)