6

I am catching all throwables in a unit test wrapper method in order to reset some data in an external system. I want to re-throw the original exception when this is done and I am using this piece of code to do it:

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

However, is there any existing utility call that does this already?

(I am catching throwables because AssertionErrors are Errors.)

Edit: To be honest, I don't really want to wrap the exception, so any trick that would allow me to throw any throwable (including checked exceptions) without declaring throws is acceptable.

billc.cn
  • 7,187
  • 3
  • 39
  • 79

3 Answers3

10

Yes, there is a way to write a method that will avoid wrapping your checked exceptions. For this use case it is a perfect match, although you should definitely be very careful with it, since it can easily confuse the uninitiated. Here it is:

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

and you'd use it as

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

As commented by Joachim Sauer, in certain cases it helps convince the compiler that the line calling sneakyThrow causes the method to terminate. We can just change the declared return type:

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

and use it like this:

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

For educational purposes it is nice to see what's going on at the bytecode level. The relevant snippet from 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;

Note there is no checkcast instruction. The method even legitimately declares to throw T, which can be any Throwable.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Any chance you could show the simplified version (no return value) as well? – Duncan Jones Nov 23 '12 at 12:07
  • Actually, throwing in the calling code is a better idea (i.e. *return* the "RuntimeException" instead of throwing it), because that way the compiler actually *knows* that the code calling `trhow` will not return. Otherwise you might run into "use of uninitialized variable" problems. – Joachim Sauer Nov 23 '12 at 12:12
  • 1
    A good way would be for the utility method to throw the exception and additionally have a return type of `RuntimeException`. This way the caller can write `throw trhow(t)` to satisfy the compiler, but the exception gets still thrown if the caller forgets to throw it. This pattern is used in the Guava library, too. – Philipp Wendler Nov 23 '12 at 12:16
  • @JoachimSauer This is an excellent idea, but it is not the one I have implemented and it is probably not even doable. I will however try to do it that way. – Marko Topolnik Nov 23 '12 at 12:16
  • @PhilippWendler It is still an open question whether this is doable. – Marko Topolnik Nov 23 '12 at 12:17
  • @MarkoTopolnik Why shouldn't it be doable? The method from my answer does exactly that. – Philipp Wendler Nov 23 '12 at 12:17
  • @PhilippWendler The method from your example is a trivial implementation of OP's code. It **wraps** the checked exception. – Marko Topolnik Nov 23 '12 at 12:18
  • @JoachimSauer Do you have code that does it? I'm on it right now. – Marko Topolnik Nov 23 '12 at 12:18
  • @MarkoTopolnik I was talking about the trick with the return type, and this works with your method just as well as with the OP's one. – Philipp Wendler Nov 23 '12 at 12:20
  • @PhilippWendler But the whole point of my code is **no wrapping**. Without that the problem reduces to a boring trifle. – Marko Topolnik Nov 23 '12 at 12:21
  • @MarkoTopolnik: sorry, I accidentally edited the wrong answer. – Joachim Sauer Nov 23 '12 at 12:34
  • This generics type trick is still quite magical to me. My understanding of Java's generics implementation is that a cast will always be inserted by the compiler. Interestingly, in the compiled byte code, the cast is only to the lower bound (Exception) as opposed to the bound type (RuntimeException) which I think did the trick. – billc.cn Nov 23 '12 at 12:41
  • 2
    @billc.cn Exactly, that's what that `@SuppressWarnings` is doing in there: it warns you that you are commiting an *unchecked cast* and from here on, Java makes no guarantees as to type safety. I read about this in general from one of the authors of Generics: only as long as you get **no warnings whatsoever** from the compiler is your generified code type-safe. In practice this never happens as it would deprive us even of the little help that Generics have to offer. – Marko Topolnik Nov 23 '12 at 12:46
8

The great Guava library has a Method Throwables.propagate(Throwable) that does exactly what your code is doing: JavaDoc

From the doc:

Propagates throwable as-is if it is an instance of RuntimeException or Error, or else as a last resort, wraps it in a RuntimeException then propagates.

Philipp Wendler
  • 11,184
  • 7
  • 52
  • 87
0

Perhaps setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) can help you. There you can define a global exception handler for all exceptions that occur.

Uwe Plonus
  • 9,803
  • 4
  • 41
  • 48