6

我在单元测试包装器方法中捕获所有 throwable,以便重置外部系统中的一些数据。完成后我想重新抛出原始异常,并且我正在使用这段代码来执行此操作:

if (t instanceof RuntimeException) {
    throw (RuntimeException) t;
} else if (t instanceof Error) {
    throw (Error) t;
} else {
    throw new RuntimeException(t);
}

但是,是否有任何现有的实用程序调用已经这样做了?

(我正在捕获 throwable,因为 AssertionErrors 是错误。)

编辑:说实话,我真的不想包装异常,所以任何允许我在不声明 throws 的情况下抛出任何可抛出(包括检查的异常)的技巧都是可以接受的。

4

3 回答 3

10

是的,有一种方法可以编写一种方法来避免包装检查的异常。对于这个用例,它是一个完美的匹配,虽然你绝对应该非常小心它,因为它很容易混淆初学者。这里是:

@SuppressWarnings("unchecked")
public static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
    throw (T) t;
}

你会用它作为

catch (Throwable t) { sneakyThrow(t); }

正如 Joachim Sauer 所评论的,在某些情况下,它有助于让编译器相信行调用sneakyThrow会导致方法终止。我们可以改变声明的返回类型:

@SuppressWarnings("unchecked")
public static <T extends Throwable> T sneakyThrow(Throwable t) throws T {
    throw (T) t;
}

并像这样使用它:

catch (Throwable t) { throw sneakyThrow(t); }

出于教育目的,很高兴看到字节码级别发生了什么。相关片段来自javap -verbose UncheckedThrower

public static <T extends java.lang.Throwable> java.lang.RuntimeException sneakyThrow(java.lang.Throwable) throws T;
  descriptor: (Ljava/lang/Throwable;)Ljava/lang/RuntimeException;
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=1, locals=1, args_size=1
       0: aload_0
       1: athrow
  Exceptions:
    throws java.lang.Throwable
  Signature: #13                          // <T:Ljava/lang/Throwable;>(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;^TT;

注意没有checkcast说明。该方法甚至合法地声明为 throw T,它可以是 any Throwable

于 2012-11-23T12:04:16.003 回答
7

伟大的Guava库有一个方法Throwables.propagate(Throwable)可以完全按照您的代码正在执行的操作:JavaDoc

从文档:

如果它是 RuntimeException 或 Error 的实例,则按原样传播 throwable,或者作为最后的手段,将其包装在 RuntimeException 中然后传播。

于 2012-11-23T12:12:50.777 回答
0

或许setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)能帮到你。在那里,您可以为所有发生的异常定义一个全局异常处理程序。

于 2012-11-23T12:01:30.273 回答