Sneakily throwing checked exceptions
Posted on 28 April 2012
I was reading the Clojure source code the other day when I noticed this curious snippet in LispReader.java:
static public int read1(Reader r){ try { return r.read(); } catch(IOException e) { throw Util.sneakyThrow(e); } }
I immediately thought to myself: “what does sneakyThrow
do?” It
looks like it is a magic way to throw a checked exception without
needing a throws
declaration. But how does it work?
First, a little background. Checked exceptions are effectively a
static analysis tool: there are no runtime checks on checked
exceptions. Rather, javac
will refuse to compile code where a
checked exception is thrown with no catch
block to catch it nor
throws
declaration to declare its propagation.
It’s possible to throw a checked exception using bytecode manipulation, or Thread.stop(Throwable), and these techniques have been known for at least a decade. However bytecode manipulation is messy, and Thread.stop(Throwable) has been deprecated for at least a decade too. Is there a pure-Java way to throw a checked exception sneakily?
C-family languages normally provide typecasts, a trapdoor to escape
their static typing system when you think it is more hindrance than
help. So a first attempt might go something like throw
(RuntimeException) e;
. However if you try this in the above code, you
will get a ClassCastException
at runtime, because IOException
is
not an instance of RuntimeException
. It would seem that there is no
pure-Java way to throw a checked exception.
So how does sneakyThrow
work? Here it is, in all its glory:
/** * Throw even checked exceptions without being required * to declare them or catch them. Suggested idiom: * throw sneakyThrow( some exception ); */ static public RuntimeException sneakyThrow(Throwable t) { // http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html if (t == null) throw new NullPointerException(); Util.sneakyThrow0(t); return null; } @SuppressWarnings("unchecked") static private void sneakyThrow0(Throwable t) throws T { throw (T) t; }
That link in the comments gives credit to Reinier Zwitserloot who, as far as I know, had the first mention of this technique in 2009 on the java posse mailing list.
What we have here is a severe abuse of Java. Util.sneakyThrow(t) calls
Util.sneakyThrow0; then within sneakyThrow0() we
cast to the parameterized type T
. In this case that type is
RuntimeException
. At runtime, however, the generic types have been
erased, so that there is no T
type anymore to cast to, so the cast
disappears.
In other words, we’ve managed to convince the compiler and the runtime that they’re seeing different things. The compiler sees the code with the cast:
throw (RuntimeException) t;
so it allows the now-unchecked exception to propagate. The runtime doesn’t see the generic types, so it sees no cast:
throw t;
and therefore it doesn’t complain about a ClassCastException
.
There was one last nagging thought I had about the original code:
throw Util.sneakyThrow(e);
Given that Util.sneakyThrow(e) throws the exception itself, why does
the calling code also use throw
? The answer is, once more, to make
the compiler happy. Without the throw
, the compiler will demand a
return
statement afterwards.
Reinier Zwitserloot added this functionality to
Project Lombok as the
@SneakyThrows
annotation, so now you can propagate checked exceptions sneakily with
minimal boilerplate. The @SneakyThrows page also summarizes neatly
some use-cases for why you would ever actually want to throw a checked
exception:
- You are calling a method which literally can never throw the
exception that it declares. The example given is
new String(someByteArray, "UTF-8")
, which declares that it throws UnsupportedEncodingException but UTF-8 is guaranteed by the Java spec to always be present. - You are implementing a strict interface where you don’t have the
option for adding a
throws
declaration, and yet throwing an exception is entirely appropriate — the canonical example is Runnable.run(), which does not throw any checked exceptions.
The first case is clear — the throws
declaration is a nuisance and
any solution to silence it with minimal boilerplate is welcome.
The second case has one common alternative: wrapping the checked
exception in a RuntimeException so that you can throw it. Both
approaches will have their critics. Wrapping an exception just to gain
the privelege of throwing it results in a stacktrace with spurious
exceptions which contribute no information about what actually went
wrong. On the other hand, throwing checked exceptions may violate the
principle of least surprise; it will no longer be enough to catch
RuntimeException
to be able to guarantee catching all possible
exceptions.
It will be up to any given project to decide which is the lesser of two evils and establish a standard on their codebase.