3

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

有一些方法可以在抛出时欺骗编译器(例如,将可抛出的对象重新抛出为未选中的实用程序类?),我已经实现了:

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

但我在“赶上时间”遇到了问题:

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

有没有办法简单地实现 unchecked Throwable,或者是RuntimeException唯一的方法?我曾想避免继承,RuntimeException因为我的 throwable 不是异常,而是 yield 指令。

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

4

3 回答 3

1

Throwable是一个检查错误,实际上应该不可能抛出这样的错误,除非它是一个 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子句,in mainnot in也没有doSomething。并且没有编译错误。在这种情况下是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(not RuntimeException, not ):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 编译器中的一个错误

于 2019-09-18T19:41:59.197 回答
1

您可以扩展Error而不是Throwable. JavaError类是未经检查的Throwable.

于 2019-09-18T20:33:28.143 回答
0

Per为什么运行时异常是未经检查的异常?(以及 Jon Skeet 无可置疑的权威;),似乎 Java 编译器内置了未经检查的异常支持,并且可能不是我可以插入的东西。

引用乔恩的帖子:

它在规范中明确,第 11.1.1 节:

RuntimeException 及其所有子类统称为运行时异常类。

未经检查的异常类是运行时异常类和错误类。

已检查异常类是除未检查异常类之外的所有异常类。也就是说,检查的异常类都是 Throwable 的子类,除了 RuntimeException 及其子类和 Error 及其子类。

所以是的,编译器肯定知道 RuntimeException。

我仍然希望其他人会提出“是的,你可以”的答案,所以我会再等几天才能结束这个问题。

于 2019-09-18T22:36:07.270 回答