代码之家  ›  专栏  ›  技术社区  ›  Tim Boemker

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

  •  4
  • Tim Boemker  · 技术社区  · 7 年前

    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 回复  |  直到 7 年前
        1
  •  1
  •   Jon Skeet    7 年前

    我相信 T 推断为 RuntimeException .我推断,通过对 throwAsUncheckedException :

    var o = ExceptionUtils.throwAs(t);
    

    。。。以及更改 throwAs 收件人:

    private static <T extends Throwable> T throwAs(Throwable t) throws T
    

    (请注意,我正在使用 var 让编译器推断类型,而无需提供任何进一步的信息。)

    编译后使用 javap -c 你可以看到 checkcast 运行期异常 :

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

    这样就可以了 throwAs公司 不要声明它抛出了其他任何东西-它只调用一个声明它抛出的方法 T 所以 运行期异常 在这种情况下。

    关于这一点,还有一些似是而非的证据 JLS section 18 ,其中包括此行:

    否则,如果界集包含throws±i,±i的每个适当上界都是 运行期异常 ,然后是Ti= 运行期异常

    无其他具体异常类型,或 Throwable ,在该节中提到。不幸的是,我发现第18节几乎无法理解,所以这实际上更多的是“是的,它模糊地支持我的理论”,而不是好的证据。

    在这个特殊的情况下,我相信它实际上对 throwAsUncheckedException 方法只显式指定类型参数:

    ExceptionUtils.<RuntimeException>throwAs(t);
    

    这就是 编译器 是发霉的。信息技术 思考 仅此而已 运行期异常 将被抛出。这个 真实的 抛出的值是传入的值。演员阵容 T 由于所有正常类型的擦除原因被忽略。。。如果将代码更改为 事实上 强制转换到 运行期异常 ,对于任何选中的异常,它都将失败。这就是为什么它需要是一个通用方法:包括满足编译器要求的强制转换,而不需要在执行时进行真正的强制转换。

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