In my last blog I announced to write something about some nice reflection magic, but it turned out that I got a little short on time, so that blog isn’t ready yet. But to keep you entertained I’d like to write something about a little hack I’ve found and instead of Reflection magic, you can read about some wild Generics magic. Not bad either, isn’t it? This hack is also really helpful!

To surprise you, let me just start with the code and have you try it yourself.

public class Unchecked { public static void rethrow( final Throwable checkedException ) { Unchecked.<RuntimeException>thrownInsteadOf( checkedException ); } @SuppressWarnings("unchecked") private static <T extends Throwable> void thrownInsteadOf(Throwable t) throws T { throw (T) t; } public static void main( final String[] args ) { // throw new IOException(); // we don’t want to declare this exception Unchecked.rethrow( new IOException() ); } }

In case you haven’t tried this: what this code does is stripping the “checked” attribute from the checked IOException! So with this little generic magic, you may now throw any checked exception without having to catch or declare it, as you can see in the main method.

Why does it work?

At first sight, this looks like an ugly hack which won’t be available in future Java versions, but will it? I dare to say there is little danger. To understand why I think so, let me get into more depth.



JLS 14.18 The throw Statement

A throw statement first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the throw completes abruptly for that reason. If evaluation of the Expression completes normally, producing a non-null value V, then the throw statement completes abruptly, the reason being a throw with value V. If evaluation of the Expression completes normally, producing a null value, then an instance V’ of class NullPointerException is created and thrown instead of null. The throw statement then completes abruptly, the reason being a throw with value V’.

What the Java Language Specification tells us in this paragraph is that the cast should be executed before the throws is executed, and in fact it is! Here is the proof:

private static <T extends Throwable> void thrownInsteadOf(Throwable t) throws T { throw (RuntimeException) t; }

When we execute our class with this changed method we get a ClassCastException. Wait: if a cast is executed before throwing the exception, how can the hack work then? I cannot cast this IOException to RuntimeException! To explain this, let’s dive into Type-Erasure. Type erasure happens when generic methods are compiled. The generic type information is erased according to the given type bounds. Applied to our hack, the rules for type erasure would make the compiler create this method signature:

private static void thrownInsteadOf(Throwable t) throws Throwable { throw (Throwable) t; }

As we can see the cast is unnecessary and can be eliminated by the compiler. Let’s test if it really is: I will change the generic method to just return the exception and have the caller throw it instead and see what happens.

public static void rethrow( final Throwable checkedException ) { throw Unchecked.<RuntimeException>thrownInsteadOf( checkedException ); } @SuppressWarnings("unchecked") private static <T extends Throwable> T thrownInsteadOf(Throwable t) { return (T) t; }

When I execute my hack with these changes, this is what I get:

Exception in thread "main" java.lang.ClassCastException: java.io.IOException cannot be cast to java.lang.RuntimeException at de.plush.brix.exeptions.Unchecked.rethrow(Unchecked.java:4)

Interesting, isn’t it? I’ve got a ClassCastException, but not where one would expect it to be. This is the proof, that the cast in the “thrownInsteadOf”-method got eliminated. It is the caller who does the cast and from what we’ve seen from the type erasure, placing the cast there is the only correct way to do it. If we think about it for a while: how else would it be possible to keep the method generic? For private methods the compiler could create synthetic methods for every generic type it is called with, but for the sake of simplicity, behavioural consistency and memory consumption, this should be avoided. For public methods it would be impossible anyway. The compiler’s behaviour is sane. Since the caller does the cast it is not executed, because the Java runtime must abort abuptly and pass the exception up the call graph until it is caught or terminates the thread. So by specification the outer cast must never be executed, no matter how bonkers it is. Which means the Java runtime’s behaviour is also sane and the compiler knows what it’s doing.

Revisiting type erasure for throws clauses:

Type erasure and throws clauses are a strange couple, no really! In order to understand how the hack works I had to shed a little light on how those two work together. To analyze this, let’s start with the signature of our generic method:

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

If I used this method like this:

Unchecked.thrownInsteadOf(new IOException());

The compiler would infer the type signature and ask me to catch or declare the Exception. However, if I used the method like this:

Unchecked.thrownInsteadOf(new RuntimeException());

Then the type interference would see it is a RuntimeException and not ask me to do any exception handling. Now here’s the surprising bit: As I stated above, type erasure would translate this method to this:



private static void thrownInsteadOf(Throwable t) throws Throwable { throw t; }

You see: This method, at runtime, would always throw checked exceptions, but the compiler would always have to force me to handle exceptions, wouldn’t it? Funny thing is: The compiler cannot, because that would break the semantics for RuntimeExceptions. This may seem inconsistent, but it is legal because of what is the really weird part: While in the Java language the unchecked RuntimeException is a subtype of the checked Exception, the Java runtime environment itself knows no such thing as a checked exception. This is why the above method will work at runtime, after type erasure has made it throw a “checked” exception type.

Sun could have avoided this by making the checked Exception a subtype of the unchecked RuntimeException, but they haven’t. God knows why. But this is why we got that sweet loophole.

Bottom line is: If I’m not heavily mistaken this is no “hack”, but perfectly legal loophole which is not likely to change in the future. This is a good thing for all those among us who are really annoyed by handling all those checked exceptions, but please don’t overuse it: Checked exceptions are really not that bad, if they are used with care.