Monday, August 2, 2010

On Removing Java Checked Exceptions By Means of Perversion

/*

What do you do in Java when you need to throw a checked exception in a context where it's not allowed, for instance when implementing a third party interface with no declared throws clause? If you're smart and its allowed you just write the code in another language. Otherwise you probably just wrap the bastard checked exception in an unchecked exception and be on your way.

But the wrapping solution a) requires an extra object allocation and b) unwrapping the original exception is a bit messy.

No, no, those justifications for what I'm about to do are horse shit rationalizations. The fact is I just want to pervert Java. I make it my job to pervert languages for their own good. Or at least, my own amusement.

Instead of "throw" meet "chuck" which turns checked exceptions into unchecked chucked exceptions. This post is an executable Java program.

Edit: This is an idea that can be traced back to Java Puzzlers by Bloch and Gafter and perhaps further. I've just refined it a bit with a bottom return and a way to catch the exception.

*/

import java.io.IOException;

public class Unchecked {

/*

The key to today's lesson in programming aberration is the fact that Java allows you to cast to a generic type parameter but can't actually check the cast at runtime due to type erasure. The following method does an unchecked cast from a Throwable to some generically specified subtype of Throwable and then immediately throws it. Neither the static nor dynamic type system ever have a chance to detect any deviant behavior such as casting to a "wrong" type. The code also lets the caller specify any return type. A previous article explains why that's useful.

*/

   @SuppressWarnings("unchecked")
   private static <T extends Throwable, A> 
     A pervertException(Throwable x) throws T {
      throw (T) x;
   }

/*

Then chuck puts some lipstick on that kinky little pig by telling it to act like it's throwing a RuntimeException. Et voilĂ , no need to declare the exception in a "throws" clause.

*/

   public static <A> A chuck(Throwable t) {
      return Unchecked.
        <RuntimeException, A>pervertException(t);
   }

/*

Some sample code shows how to use chuck.

*/

   public static int testChuck() {

/*

We can't throw an IOException because it's checked

*/

      // throw new IOException("checked, oh noes");

/*

But we can chuck an IOException

*/

      return chuck(new IOException("unchecked, hellsyeah"));

/*

And, just like with a throw the next line won't compile because it's unreachable.

*/

      // System.out.println("dead code");
   }

/*

If you never want to catch the exception or you want to handle it with a top level "catch Exception" then that's all there is to it. But if you want to catch and handle the IOException specifically then there's one tiny flaw in the system. Sadly, the following won't compile because IOException isn't statically known to be thrown by testChuck() and Java wouldn't want you to catch something that can't be thrown now would it?

*/


//   public static int wontCompile() {
//      try {
//         testChuck();
//      } catch (IOException e) {
//         System.out.println("Why did you leave me with" + 
//                            " the sad clown of life?");
//      }
//   }

/*

The fix is to add a way to tell the compiler what exceptions we might chuck, kinda like the "throws" keyword but this one chucks. Totally different. The chucks method threatens to throw a T but in a sudden fit of shame it does nothing - per a tip from a commenter it takes a class argument to avoid Java's hideous generic method invocation syntax.

*/

   public static <T extends Throwable> 
     void chucks(Class<T> clazz) throws T {}

/*

And here we go, we can catch our chucked exception.

*/

   public static void main(String[] args) {
      try {
         chucks(IOException.class);

         testChuck();         
      } catch (IOException e) {
         System.out.println("Caught chucked exception " + 
                             e + ".");
      }
   }
}

/*

Running that should display "Caught chucked exception java.io.IOException: unchecked, hellsyeah."

My depraved perversion work is done. Now I may rest. I leave you with this imponderable: how many unchecked checked exceptions will Unchecked.chuck() chuck?

*/

12 comments:

Travis said...

I can't wait to add this code to our project!

Bas de Bakker said...

Most of this is in puzzle 43 of Java Puzzlers.

JamesIry said...

Thanks! I've updated with a source of the origin of the idea.

Tom said...

Very cute. I would modify the chucks() method like so:

public static
void chucks(Class clazz) throws T {
return;
}

to make for slightly more readable syntax:

Unchecked.chucks(IOException.class);

thebetathings said...

Is this Scala Either in Java?

James Iry said...

It's kinda the opposite of Either. Either (which can be found in ML, Haskell, and Scala) carries its alternatives in the type system where this article shows how to remove something from Java's type system.

crowne said...

Lombok's @SneakyThrows achieves the same result in what I think is a less perverted way. See http://projectlombok.org/features/SneakyThrows.html

JP@ classpath in Java said...

Great postt you have covered the topic quite well , I have also shared my view as Difference between checked and unchecked exception in Java let me know how do you find it.

James Iry said...

Nice! Editing the code now.

Douglas Campos (qmx) said...

amazing!

usr said...

Very entertaining read!

I have a suspicion that you made up the whole piece only to be able to write this gem of a line:

[code]A pervertException(Throwable x) throws T {[/code]

Tim McCormack said...

This should be in a JAR in Maven Central. Mind if I package it up, with or without the literate style? Or have you already done so?



This would be very useful from Clojure, which compiles to JVM bytecode
but doesn't share Java's views on checked exceptions. Specifically, it
would be useful for Java consumers of Clojure jars.