Java的检查异常处理机制是否存在问题?

6

java.lang.Exception类的javaDocs中:

如果方法或构造函数在执行过程中可能抛出已检查异常并且在方法或构造函数边界外传播,则需要在其throws子句中声明已检查异常。

但考虑以下代码:

package other;

public class CheckedExceptionHandling {

    private static <E extends Exception> void throwException() throws E {
        throw (E) new CheckedException2(); // unchecked cast warning
    }

    private static void setUncaughtExceptionHandler() {
        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
            System.out.println("Unhandled exception: " + e.getClass()); // reports CheckedExceptionHandling$CheckedException2
        });
    }

    public static void main(String[] args) /* no checked exceptions declared! */ {
        setUncaughtExceptionHandler();
        try {
            CheckedExceptionHandling.<CheckedException1>throwException();
        } catch (CheckedException1 e) {
            System.out.println(e); // never gets here
        }
    }
    // checked exceptions:
    public static class CheckedException1 extends Exception {}
    public static class CheckedException2 extends Exception {}

}

编译时出现一个警告,运行结果如下:

未处理的异常: class other.CheckedExceptionHandling$CheckedException2

我期望会出现以下一种情况:编译时错误 未报告的异常 CheckedException2; 必须捕获或声明为抛出 或者 不兼容类型: CheckedException2 无法转换为 CheckedException1 至少在运行时出现 ClassCastException
但是,编译器允许检查异常未被处理、未被声明并且传播到方法 main 的未捕获异常处理程序之外
为什么?难道我漏掉了什么吗?


加一。很好的问题,写得很好。Java异常处理有问题。最终你会习惯并放松下来。 - Bathsheba
1
这里没有任何问题。一切都符合规格。 - Tunaki
2
这看起来很像“偷偷扔”。在您选择的搜索引擎上搜索该术语,例如https://www.reddit.com/r/programming/comments/2x41h4/sneaky_exceptions_in_java/。 - Andy Turner
1
编译器明确警告您正在进行一个“未经检查的转换”。破损的不是Java的异常处理,而是您的程序。开个玩笑,Java的已检查异常是一个巨大的混乱。 - Marko Topolnik
3
请参见https://dev59.com/gGYq5IYBdhLWcg3w-Vdl。 - Tunaki
2
@Yuri,你将你的CheckedException2转换为CheckedException1,你的throwException()声明它会抛出一个CheckedException1,你的代码也捕获了一个CheckedException1 - 所以这不会是一个编译时错误。然而,当使用泛型时,将(E) new CheckedException2()强制转换为CheckedException1会产生一些严重的影响,这在Tunaki提供的链接中有描述。 - nos
1个回答

0
问题在于编译器不会超越方法边界。在 throwsException() 中,除了“extends Exception”之外,没有关于 E 的信息,因此编译器无法证明转换是错误的,并给出警告而不是错误(正如 Marko 在评论中指出的那样)。
在 main() 中,编译器只看到“throwsException”的签名,该签名被定义为“throws E”,而在 main() 中,E 是 CheckedException。由于 CheckedException 被捕获,所以一切都很好。
然后,Java 在编译时丢弃所有泛型类型信息,因此字节码中不存在对 E 的转换。因此,运行时也无法检测到类型错误。
这里出现问题的不是异常处理,而是类型系统。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接