JUnit 5使用以下代码屏蔽已检查的异常:
public static RuntimeException throwAsUncheckedException(Throwable t) {
Preconditions.notNull(t, "Throwable must not be null");
ExceptionUtils.throwAs(t);
// Appeasing the compiler: the following line will never be executed.
return null;
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> void throwAs(Throwable t) throws T {
throw (T) t;
}
在对throwAs的调用中,Java如何决定类型变量T的值?
更重要的是,此代码如何屏蔽已检查的异常?
答案 0 :(得分:1)
我认为T
被推断为RuntimeException
。我推断,通过对throwAsUncheckedException
的代码进行以下更改:
var o = ExceptionUtils.throwAs(t);
...并将throwAs
的声明更改为:
private static <T extends Throwable> T throwAs(Throwable t) throws T
(请注意,我正在使用Java 10中的var
让编译器推断出类型而不提供任何进一步的信息。)
编译完成后再使用javap -c
,您可以看到checkcast
到RuntimeException
:
invokestatic #2 // Method throwAs:(Ljava/lang/Throwable;)Ljava/lang/Throwable;
checkcast #3 // class java/lang/RuntimeException
astore_1
这使得throwAs
没有声明它抛出任何其他东西是好的 - 它只调用一个方法来声明它抛出T
,所以在这种情况下RuntimeException
。
另外有一些似是而非的证据就是JLS section 18,其中包括这一行:
否则,如果绑定集包含抛出αi,并且αi的每个正确上限是
RuntimeException
的超类型,则Ti =RuntimeException
。
该部分未提及其他具体异常类型或Throwable
。不幸的是,我发现第18节几乎无法穿透,所以这更像是“是的,模糊地支持我的理论”,而不是好的证据。
在这种特殊情况下,我认为throwAsUncheckedException
方法实际上很好(并且在理解方面更简单)只是明确指定类型参数:
ExceptionUtils.<RuntimeException>throwAs(t);
这就是编译器被软化的方式。它认为只会抛出RuntimeException
。抛出的实际值是传入的任何内容。对于所有正常类型擦除原因,将忽略对T
的强制转换...如果将代码更改为实际强制转换为RuntimeException
,则对于任何已检查的异常,它都将失败。这就是为什么它需要是一个通用的方法:包含一个满足编译器的转换,而不是在执行时真正进行转换。
JVM允许这样做,因为据我所知,已检查的异常纯粹是编译器方面,并且在JVM本身中没有对它们进行验证。它只知道抛出异常。