Saturday, December 11, 2010

Throw a checked exception from a method without declaring it

When you try to throw a checked exeption from a method in Java, the compiler will force you to either catch it or declare it to be thrown in the method declaration. For example, the following method fails to compile:
public void go(){
    throw new IOException();
}
The compiler says:
Dummy.java:15: unreported exception java.io.IOException;
must be caught or declared to be thrown
    throw new IOException();
    ^
1 error
However, if you declare it to be thrown in the method declaration, like this:
public void go() throws IOException{
    throw new IOException();
}
then it compiles successfully.

But is it possible to throw a checked exception WITHOUT declaring it to be thrown? You could wrap it in a RuntimeException (for example, new RuntimeException(new IOException()), but then you lose the ability to catch the checked exception in the caller method, because you have changed the type of the exception.

There is another trick you can use. I first noticed this being done in the JDK's java.lang.Class#newInstance0 method:

private T newInstance0()
    throws InstantiationException, IllegalAccessException
{
    ...
    // Run constructor
    try {
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
    ...
}
As shown above, the method throws an InvocationTargetException but only declares the exceptions InstantiationException and IllegalAccessException to be thrown! The Sun utility class sun.misc.Unsafe is being used to throw a checked exception without declaring it. We could use the same approach, but unfortunately our code throws a security exception when we try to use it: java.lang.SecurityException: Unsafe. However, we can bypass this by using reflection to get hold of the unsafe variable directly, as shown below:
public void go() {
  getUnsafe().throwException(new IOException());
}

private Unsafe getUnsafe() {
  try {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    return (Unsafe) field.get(null);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}
So, why did the JDK developers do it this way? I don't know, but I'm sure they had a good reason. Although this is a neat trick to impress your friends, I wouldn't recommend using it. This is non-standard, undocumented, Sun-specific code which could change in future releases and may not be compatible with all JVMs. It also makes code harder to understand for others and violates the POLA.

Further Reading:
Avoiding Checked Exceptions [O'Reilly]

2 comments:

  1. Anonymous10:32 AM

    Hi Fahd,

    What JDK version did you use for this example?
    I'm now using 1.6.0_21

    The method getUnsafe() has explicity declared return type "Unsafe". And the compiler prohibits me to refer to (or import) this type

    Iurii Volchyn
    iurii.volchyn@gmail.com

    ReplyDelete
  2. Anonymous10:50 AM

    Hi Fahd,

    I've found out that I've got the compiler-time error because I've used the Eclipse's excetion environment instead of the "plain" JDK.

    When I updated my settings your example started to work fine.

    Thank you very much for your example.

    Regards,
    Iurii Volchyn
    iurii.volchyn@gmail.com

    ReplyDelete