【问题标题】:How to throw an Exception when your method signature doesn't allow to throw Exception?当您的方法签名不允许抛出异常时如何抛出异常?
【发布时间】:2012-08-10 04:37:12
【问题描述】:

我有这样的方法:

public void getSomething(){
...
}

我想在getSomething() 中抛出一个Exception。编译器不允许我这样做,因为我的方法不允许将Exception 扔在那里。但是我需要为我的测试抛出一个 Exception 的子类(我不能抛出 Unchecked Exception。这显然是一个 hack,但我需要它来进行测试。我尝试了 EasyMock,但它也不允许我这样做。任何想法如何做到这一点?

谢谢, 肖恩·阮

【问题讨论】:

  • 您正在测试一些不可能发生的事情。为什么?
  • 我尝试使用 LogicalHandler 模拟肥皂响应。 handleMessage(LogicalMessageContext context) 没有声明抛出异常。但是肥皂端点抛出的所有异常都是异常的子类。我需要抛出这些异常来模拟我的服务响应。
  • 如果您有兴趣,请查看我的编辑。
  • 为什么不能抛出未经检查的异常?我不明白你想做什么。你使用什么样的测试框架?您可以在测试中正常捕获未经检查的异常。

标签: java exception checked-exceptions


【解决方案1】:

方法一:

This post by 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;
    }
}

这个技巧依赖于 throwUnchecked 向编译器“撒谎”类型 ERuntimeException 并调用 throwAny。由于throwAny 被声明为throws E,编译器认为特定的调用可以直接抛出RuntimeException。当然,这个技巧是通过throwAny 任意声明E 并盲目地转换为它来实现的,允许调用者决定它的参数被转换为什么 - 理智编码时的糟糕设计。在运行时,Eerased,没有任何意义。

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


方法二:

您也可以为此使用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() 通常会抛出 SecurityException。一旦你有了Unsafe 的实例,你就可以使用它的可怕功能:

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

致谢 this answer 在帖子 https://stackoverflow.com/questions/5574241/interesting-uses-of-sun-misc-unsafe。我想我会为了完整性而提到这一点,但最好只使用上面的技巧而不是允许 Unsafe 进入你的代码。


方法三:

在有关使用 Unsafe@bestsss points out 的链接答案的 cmets 中,使用已弃用的方法 Thread.stop(Throwable) 是一个更简单的技巧:

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

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

【讨论】:

  • @PaulBellora 你总是可以使用 Class::newInstance() 方法,它可以抛出任何 ctor 所做的事情
  • @PaulBellora 为什么第一个示例在运行时没有ClassCastException
  • @Demetri (E)e 是未经检查的强制转换,这意味着如果错误,JVM 不能快速失败。类型擦除后,该方法与 private static void throwAny(Throwable e) throws Throwable { throw e; } 无法区分。
  • Unsafe 就好了
  • @Enerccio 直到 Java 9
【解决方案2】:

Java 有两种异常,已检查和未检查。您必须声明已检查的异常,但您不必声明未检查的异常。

RuntimeException 是基本的未经检查的异常,因此您可以在不声明的情况下抛出它。

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

附带说明一下,您可能不想抛出原始的 RuntimeException,而是将其子类化为更符合您需求的东西。 RuntimeException 的任何子类也将被取消选中。

【讨论】:

  • 很抱歉不清楚。我需要抛出 java.lang.Exception 的子类(检查异常)。
  • @SeanNguyen RuntimeException 是异常的子类。这只是 Java 中的一种特殊情况,您不需要声明它会被抛出。
  • 对不起,又不是很清楚。我需要抛出作为异常子类的已检查异常。我不能抛出 RuntimeException。
  • @SeanNguyen:请告诉我们为什么需要抛出一个检查异常。
【解决方案3】:

我不知道你想做什么。您可以在方法中抛出未经检查的异常,然后使用 JUnit 进行测试,如下面的示例所示。

public void getSomething() {
    throw new RuntimeException();
}

JUnit 4 测试示例:

@Test
public void testGetSomething() {
    try {
        getSomething();
        fail("Expecting RuntimeException!");
    } catch (Exception e) {
        Logger.getLogger("test").info("Exception was caught: " + e.getClass());
    }
}

【讨论】:

    猜你喜欢
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    相关资源
    最近更新 更多