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 main
not 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 编译器中的一个错误。