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

时间:2018-04-09 11:24:30

标签: junit5

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

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

1 个答案:

答案 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,您可以看到checkcastRuntimeException

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本身中没有对它们进行验证。它只知道抛出异常。