junit5 – JUnit如何屏蔽已检查的异常?

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的值?

更重要的是,此代码如何屏蔽已检查的异常?

最佳答案 我相信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之后,您可以看到RuntimeException有一个checkcast:

invokestatic  #2  // Method throwAs:(Ljava/lang/Throwable;)Ljava/lang/Throwable;
checkcast     #3  // class java/lang/RuntimeException
astore_1

这使得throw更好,因为它不会声明它抛出任何其他内容 – 它只调用一个声明它抛出T的方法,所以在这种情况下运行RunException.

进一步有点似是而非的证据是在JLS section 18,其中包括这一行:

Otherwise, if the bound set contains throws αi, and each proper upper bound of αi is a supertype of RuntimeException, then Ti = RuntimeException.

该部分未提及其他具体异常类型或Throwable.不幸的是,我发现第18节几乎无法穿透,所以这更像是“是的,模糊地支持我的理论”而不是好的证据.

在这种特殊情况下,我认为throwAsUncheckedException方法实际上很好(并且在理解方面更简单)只是明确指定类型参数:

ExceptionUtils.<RuntimeException>throwAs(t);

这就是编译器被软化的方式.它认为只会抛出RuntimeException.抛出的实际值是传入的任何值.对于所有正常类型擦除原因,将忽略对T的强制转换…如果将代码更改为实际强制转换为RuntimeException,则对于任何已检查的异常都将失败.这就是为什么它需要是一个通用的方法:包含一个满足编译器的转换,而不是在执行时真正转换.

JVM允许这样做,因为据我所知,已检查的异常纯粹是编译器方面,并且在JVM本身中没有对它们进行验证.它只知道抛出异常.

点赞