Home > Reflection and Generics > Hack of the day: Unchecking checked exceptions.

Hack of the day: Unchecking checked exceptions.

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.

Advertisements
  1. chris
    April 30, 2010 at 13:24

    But isnt the stacktrace erased when you retrow the exception, thus removing a lot of the interesting metadata from the exception object itself ? Thats why I dont like to retrow exceptions in the first place. So throwing a “new RuntimeException(originalException)” is better because it also converts the exception to unchecked, still keeps the metainformation about the originalException. Dont you think ?

    • April 30, 2010 at 20:48

      If you caught the exception and just threw a new one, the stack trace would be lost. If you caught the exception and threw a new one with the original as cause the stacktrace would be cluttered. But this solution just rethrows the existing exception, which is brilliant! This is the stacktrace from the example:

      Exception in thread "main" java.io.IOException
       at de.plush.brix.exeptions.Unchecked.main(Unchecked.java:18)
      

      Let’s move the exception to a new method and see if something else gets lost:

       static void foo(){
        Unchecked.rethrow(new IOException());
       }
      
       public static void main(final String[] args){
        foo();
       }
      

      Here is the stacktrace:

      Exception in thread "main" java.io.IOException
       at de.plush.brix.exeptions.Unchecked.foo(Unchecked.java:17)
       at de.plush.brix.exeptions.Unchecked.main(Unchecked.java:21)
      

      The stacktrace points to the real origin of the exception, looks slick and clean and no relevant data is lost.

  2. Stephan
    May 19, 2010 at 14:57

    Besides this being a very interesting look into Java internals, I’m a little puzzled as to what would be a valid use case for this.

    Handling exceptions anywhere else than where they are thrown always decreases the readability of the code. Handling checked exceptions in code that apparently is incapable of throwing this exception because it is not specified (you would have to read all the called methods, identify the rethrow statement and understand it) seems an even worse idea to me.

    Since the exception is not declared, it becomes uncatchable. If you try to put any statement that potentially could throw such an exception into a try/catch, the compiler will warn you about an unreachable code block. You’ll have to catch a unchecked exception (like “Exception”) and than identify the correct type with the instanceof operator.

    It is interesting and there might be uses cases for it. But you should put a huge warning sign on any occurrence in your code.

    • May 19, 2010 at 20:32

      Stephan, as always you bring up the most painful subject: The “unreachable code” compiler error. It is true that it spoils most of the fun.

      Anyway, there is an ongoing debate over checked exceptions, and especially Bruce Eckel and Andreas Hejlsberg make some good points there:

      http://www.mindview.net/Etc/Discussions/CheckedExceptions
      http://www.artima.com/intv/handcuffs.html

      One interesting point in this debate is the idea that there should be a centralized exception handling further up the stack, which would boil down to some general “catch(Exception)” with a user notice. See the last paragraph of page 2 in the Hejlsberg interview and the protection against open resources could be done in finally-blocks, which I consider a controversial idea, but not entirely stupid either.

      Also several projects have (for the similar reasons as brought up in the neverending debate) started to discourage checked exceptions entirely and wrap them with RuntimeExceptions. the catch blocks would make no difference to the approach with using this hack:

      try{...
      } catch(WrapperRuntimeException e){
       if(e.getCause() instanceof IOException){
        //...
       }else if (e.getCause() instanceof NumberFormatException){
        //..
       }
       //...
      }
      

      But for projects like this, this hack could be the cleaner variant, as it gets rid of some of the other CheckedException problems (scalability, breaking client code, etc).

      Anyway, I have yet to try this in a small hobby-project to see if it adds any value. Maybe someone will find an elegant solution to handle the exceptions nicely in the future.

  3. MichaelloDN
    May 27, 2010 at 12:11

    I would appreciate more visual materials, to make your blog more attractive, but your writing style really compensates it. But there is always place for improvement

    • May 31, 2010 at 13:29

      Thank you very much for your kind feedback. I hope I will find the time to get a decent free UML-Editor to spice up my forthcoming blogs. So far I have tried a few, but they were either very clumsy to use (Argo, Objecteering) did not install properly (eUML2) or there were other annoyances like having to officially apply for a free version. I guess I’ll just use “Violet UML”, which may have no round trip engineering or fancy things like that, but is very easy to use.

  4. September 15, 2010 at 14:44

    In case anyone’s interested: Here is a nice posting on DZone that mentions other methods to throw checked Exceptions without explicitly handling them:
    http://java.dzone.com/articles/throwing-undeclared-checked

  1. May 3, 2010 at 22:20
  2. July 14, 2016 at 12:30

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: