如何在 java 中编写未经检查的 Throwable

How to write an unchecked Throwable in java

我想编写一个未选中的自定义 Throwable。

有一些方法可以在抛出时欺骗编译器(例如 Utility class that re-throws a throwable as unchecked?),我已经实现了这些方法:

public class CustomThrowable extends Throwable {
    public CustomThrowable(String message) {
        super(message);
    }
    @SuppressWarnings("unchecked")
    public <T extends Throwable> T unchecked() throws T {
        throw (T) this;
    }
}

但我 运行 遇到了 "catch time" 的问题:

try { 
    throw new CustomThrowable("foo").unchecked();
}
catch (CustomThrowable t) { } // <- compiler error because the try block does not "throw" a CustomThrowable

有没有一种方法可以简单地实现未检查的 Throwable,或者 RuntimeException 是唯一的方法吗?我曾想避免从 RuntimeException 继承,因为我的 throwable 不是异常,而是 yield 指令。

更新: 避免扩展 RuntimeException 的另一个原因是我的 CustomThrowable 会被通用 catch (Exception ex) { } 块捕获。因此,如果我想从堆栈内部传递信息,每一层都需要潜在地意识到 CustomThrowable 可能会通过并明确地捕获 - 重新抛出它;这种意识是人们在使用 Throwable 设计时试图避免的很大一部分。

Throwable 是一个 checked 错误,实际上 should 不可能抛出这样的错误,除非它是 RuntimeException 或错误。

但实际上这是可能的。这是一个例子。

这是一个实用程序,它抛出 任何 未经检查的异常,如已检查:

package org.mentallurg;

public class ExceptionUtil {

    private static class ThrowableWrapper extends Throwable {

        private Throwable throwable;

        public ThrowableWrapper(Throwable throwable) {
            super();
            this.throwable = throwable;
        }

        @SuppressWarnings("unchecked")
        public <T extends Throwable> T throwNested() throws T {
            throw (T) throwable;
        }
    }

    private static <T extends Throwable> T throwThis(T throwable) throws T {
        throw throwable;
    }

    public static <T extends Throwable> void throwUnchecked(T throwable) {
        new ThrowableWrapper(throwable).throwNested();
    }

}

这是一个用法示例:

package org.mentallurg;

public class Test {

    private static void doSomething() {
        Exception checkedException = new Exception("I am checked exception");
        ExceptionUtil.throwUnchecked(checkedException);
    }

    public static void main(String[] args) {
        doSomething();
    }

}

注意没有 throws Throwable 子句,maindoSomething 中都没有。而且没有编译错误。在 RuntimeException 的情况下是可以理解的,但在 Throwable.

的情况下则不然

如果我们执行它,我们得到以下信息:

Exception in thread "main" java.lang.Exception: I am checked exception
    at org.mentallurg.Test.doSomething(Test.java:6)
    at org.mentallurg.Test.main(Test.java:11)

它是如何工作的?

最重要的部分是这个:

new ThrowableWrapper(throwable).throwNested();

其实这里的方法throwNested可以抛出一个Throwable。这就是为什么 Java 编译器 应该 引发错误并且 应该 要求这行代码被 try/catch 包围或者Throwable 子句应添加。但事实并非如此。为什么?我相信这是 Java 编译器的一个缺陷。其他线程中关于 SO 的一些评论提到了类型擦除,但它们是不正确的,因为类型擦除在 运行 时间期间是相关的,我们正在谈论 编译次.

有意思的是,反编译后的代码显示,这里会抛出一个Throwable(不是RuntimeException,不是Error):

  public static <T extends java.lang.Throwable> void throwUnchecked(T);
    Code:
       0: new           #28                 // class org/mentallurg/ExceptionUtil$ThrowableWrapper
       3: dup
       4: aload_0
       5: invokespecial #30                 // Method org/mentallurg/ExceptionUtil$ThrowableWrapper."<init>":(Ljava/lang/Throwable;)V
       8: invokevirtual #32                 // Method org/mentallurg/ExceptionUtil$ThrowableWrapper.throwNested:()Ljava/lang/Throwable;
      11: pop
      12: return

此行为并非特定于 Throwable。我们可以用选中的 Exception 替换它,并会得到相同的结果:

public <T extends Exception> T throwNested() throws T {

在所有这些情况下,如果我们用特定的 类 替换泛型,那么 Java 编译器将报告错误,这是正确的。在使用泛型 Java 的情况下,编译器会忽略已检查的异常并且不会报告任何错误。这就是为什么我认为这是 Java 编译器 中的一个 错误

您可以扩展 Error 而不是 Throwable。 Java Error class 是未选中的 Throwable.

根据 Why runtime exception is unchecked exception?(以及 Jon Skeet 无可置疑的权威 ;),java 编译器似乎内置了未经检查的异常支持,我可能无法插入。

引自 Jon 的 post:

It's explicitly in the specification, section 11.1.1:

RuntimeException and all its subclasses are, collectively, the runtime exception classes.

The unchecked exception classes are the runtime exception classes and the error classes.

The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are all subclasses of Throwable other than RuntimeException and its subclasses and Error and its subclasses.

So yes, the compiler definitely knows about RuntimeException.

我还是希望有人能给出一个"yes you can"答案,所以我再等几天再结束这个问题。