我想写一个自定义的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;
}
}
但我在“赶时间”的时候遇到了问题
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
可能会通过并显式地捕获-重新抛出它;当使用X1 M7 N1 X设计时,这种意识是人们试图避免的大部分。
4条答案
按热度按时间bmp9r5qi1#
您可以扩展
Error
而不是Throwable
。JavaError
类是一个未检查的Throwable
。bogh5gae2#
Throwable
是一个checked错误,实际上它 * 应该 * 不可能抛出这样的错误,除非它是一个RuntimeException或Error。但实际上这是可能的。这里有一个例子。
下面是一个将anyunchecked异常作为checked抛出的实用程序:
下面是一个使用示例:
请注意,没有
throws Throwable
子句,main
和doSomething
中都没有。而且没有编译错误。在RuntimeException
的情况下,这是可以理解的,但在Throwable
的情况下就不是了。如果我们执行它,我们得到以下内容:
它是如何工作的?
最重要的部分是这一个:
实际上,这里的
throwNested
方法 * 可以 * 抛出一个Throwable
。这就是为什么Java编译器 * 应该 * 抛出一个错误,* 应该 * 要求这行代码用try/catch包围,或者应该添加Throwable
子句。但事实并非如此为什么?我相信这是Java编译器中的一个缺陷。其他线程中关于SO的一些评论提到了类型擦除,但它们是不正确的,因为类型擦除在 * 运行 * 时间是相关的,而我们正在谈论 * 编译 * 时间。有趣的是,反编译后的代码显示,这里将抛出
Throwable
(不是RuntimeException
,也不是Error
):这种行为并不特定于
Throwable
。我们可以将其替换为选中的Exception
,并得到相同的结果:在所有这些情况下,如果我们用特定的类替换泛型,那么Java编译器将报告错误,这是正确的。在泛型的情况下,Java编译器忽略检查的异常并且不报告任何错误。这就是为什么我认为这是Java编译器中的一个bug。
utugiqy63#
Per Why runtime exception is unchecked exception?(以及Jon Skeet不容置疑的权威;),看起来unchecked-exception支持是内置在java编译器中的,可能不是我可以插入的东西。
引用Jon的文章:
它在规范第11.1.1节中明确指出:
RuntimeException及其所有子类共同构成运行时异常类。
未检查的异常类是运行时异常类和错误类。
检查的异常类是除未检查的异常类之外的所有异常类。也就是说,检查的异常类是Throwable的除RuntimeException及其子类和Error及其子类之外的所有子类。
所以是的,编译器肯定知道RuntimeException。
我仍然希望其他人会给出一个“是的,你可以”的答案,所以我会再等几天再结束这个问题。
vcudknz34#
这种方法与mentallurg的方法基本相同,但它避免了不必要的 Package 类。
在您的代码中:
为了完整起见,下面是生成的字节码用于比较:
如果
throwUnchecked0
接受T
类型的参数而不是任何Throwable
,那么对throws子句或try/catch语句的要求将传播到callers 1。有趣的是,在大多数情况下,这与泛型的行为非常不同。例如,如果代码被更改,使得内部方法返回
T
而不是抛出T
,然后公共方法抛出该返回值,则T
的类型被推断为Throwable
,并且throws子句要求将被传播。因此,这似乎确实是Java编译器中的一个bug,尽管我希望看到它仍然存在,因为它仍然可以直接在字节码中使用(其他JVM语言如Kotlin广泛使用),并且在某些情况下可能很有用。未经检查的
T
转换是完全安全的,因为T
在编译时被擦除并替换为Throwable
,这将始终成功,因为参数是Throwable
。对公共API隐藏真实的的实现并不是绝对必要的。它只是为了让读者清楚(并强制)泛型类型是完全忽略的,并从调用站点中省略。
1调用者还可以显式地为泛型参数指定类型,并使throws子句的要求传播。这就是使用公共 Package 方法的部分原因。
2好吧,只是在这个意义上,它永远不会在运行时失败。毕竟,我们正在抛出未经检查的异常。