当您的方法签名不允许抛出异常时,如何抛出异常?

2022-09-02 01:22:23

我有一个这样的方法:

public void getSomething(){
...
}

我想扔一个里面.编译器不允许我这样做,因为我的方法不允许在那里抛出。但是我需要为我的测试抛出一个子类(我不能抛出未检查的异常)。这显然是一个黑客,但我需要它进行测试。我尝试过EasyMock,但它也不允许我这样做。任何想法如何做到这一点?ExceptiongetSomething()ExceptionException

谢谢,肖恩·阮


答案 1

方法 1:

Alexey Ragozin的这篇文章描述了如何使用泛型技巧来抛出一个未声明的检查异常。从那篇文章:

public class AnyThrow {

    public static void throwUnchecked(Throwable e) {
        AnyThrow.<RuntimeException>throwAny(e);
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable> void throwAny(Throwable e) throws E {
        throw (E)e;
    }
}

该技巧依赖于对编译器的“撒谎”,即该类型具有对 .由于 被声明为 ,编译器认为特定的调用可以只抛出 。当然,这个技巧是通过任意声明和盲目地投射到它来实现的,允许调用者决定其参数被投射到什么 - 在理智编码时的可怕设计。在运行时,被擦除并且没有意义。throwUncheckedERuntimeExceptionthrowAnythrowAnythrows ERuntimeExceptionthrowAnyEE

正如你所指出的,做这样的事情是一个巨大的黑客,你应该很好地记录它的使用。


方法 2:

您也可以为此目的使用。首先,您必须实现一个使用反射返回该类的实例的方法:sun.misc.Unsafe

private static Unsafe getUnsafe() {
    try {
        Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeField.setAccessible(true);
        return (Unsafe)theUnsafeField.get(null);
    }
    catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    }
    catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

这是必要的,因为调用通常会抛出一个 .一旦你有了实例,你可以把它可怕的功能用在:Unsafe.getUnsafe()SecurityExceptionUnsafe

Unsafe unsafe = getUnsafe();
unsafe.throwException(new Exception());

功劳归于帖子 https://stackoverflow.com/questions/5574241/interesting-uses-of-sun-misc-unsafe 上的这个答案。我想我会提到这一点以保持完整性,但最好只是使用上面的技巧,而不是允许进入你的代码。Unsafe


方法 3:

在关于使用 的链接答案的评论中,@bestsss指出了使用已弃用的方法 Thread.stop(Throwable) 的一个更简单的技巧:Unsafe

Thread.currentThread().stop(new Exception());

在这种情况下,您将非常激烈地使用并再次记录。同样,我更喜欢第一个技巧,因为它的(相对)清洁度。@SuppressWarnings("deprecation")


答案 2

Java 有两种异常:选中和未选中。必须声明已选中的异常,但不必声明未选中的异常。

RuntimeException是基本的未选中异常,因此您可以在不声明它的情况下引发它。

public void getSomething(){
   throw new RuntimeException("I don't have to be declared in the method header!");
}

作为旁注,您可能不想抛出原始的 RuntimeException,而是将其子类化为更特定于您的需求的内容。RuntimeException 的任何子类也将被取消选中。