2013-12-31

Handling exceptions with Guava

Some time ago, while reading Java Concurrency In Practice, I encountered the following utility method:
public static RuntimeException launderThrowable(Throwable t) {
  if (t instanceof RuntimeException)
    return (RuntimeException) t;
  else if (t instanceof Error)
    throw (Error) t;
  else
    throw new IllegalStateException("Not unchecked", t);
}
At first it looks quite strange, so let's see how it can be used.

Suppose you have a method with can throw many different exception types (or simply throws most general java.lang.Exception or even java.lang.Throwable). If you want to handle only some exception types and propagate the others, you can write:
String foo() {
  try {
    return riskyMethod();
  } catch (SomeException e) {
    return handleSomeException(e);
  } catch (AnotherException e) {
    return handleAnotherException(e);
  } catch (Exception e) {
    throw launderThrowable(e);
  }
}
launderThrowable() returns RuntimeException, so you can write throw launderThrowable() and the compiler won't complain about missing return statement. It doesn't throw checked exceptions (it wraps them in RuntimeExceptions), so you don't need to declare them as thrown in your method declaration. It encapsulates some messy exception-related logic and makes it look clean outside. Because of that, it a useful utility for handling exceptions.

But it turns out, that you don't need to write this method yourself. Google's Guava library already contains class Throwables with method propagate(), which does exactly the same thing. For the sake of completeness, this is the previous example, rewritten with Guava:
String foo() {
  try {
    return riskyMethod();
  } catch (SomeException e) {
    return handleSomeException(e);
  } catch (AnotherException e) {
    return handleAnotherException(e);
  } catch (Exception e) {
    throw Throwables.propagate(e);
  }
}
The class Throwables contains also other useful methods - you can check the official docs for more information. They show interesting usage examples and "dos and don'ts".

One more thing - if you are worried that using launderThrowable() or Throwables.propagate() breaks/pollutes your stacktraces in case of RuntimeExceptions (beacause they are caught and rethrown), you don't need to. Exception's stacktrace is created when exception object is instantiated and remains unaffected by throwing, catching or passing it around.