Throwing Undeclared Checked Exceptions
Join the DZone community and get the full member experience.
Join For FreeSometimes checked exceptions can be a problem. For instance, recently I tried to implement some common logic to retry failing network operations and it resulted in a kind of command pattern on which, as usual, the execute() method throws java.lang.Exception. That complicated the caller code which has to catch and handle java.lang.Exception instead of the more specific exceptions...
I knew that checked exceptions are enforced by the compiler, while in the virtual machine there is nothing preventing a checked exception to be thrown by a method not declaring it, so I started to check on internet how to implement this.
I found two posts on Anders Noras's blog (#1 #2) on how to perform this magic.
Method #1: the sun.misc.Unsafe class
import java.lang.reflect.Field; import sun.misc.Unsafe; public class UnsafeSample { public void methodWithNoDeclaredExceptions( ) { Unsafe unsafe = getUnsafe(); unsafe.throwException( new Exception( "this should be checked" ) ); } 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); } } public static void main( String[] args ) { new UnsafeSample().methodWithNoDeclaredExceptions(); } }
This makes use of internal Sun JRE libraries implementation classes. It could not work if you use a non Sun VM. And in fact it doesn't if you use GCJ (The GNU compiler for Java).
The getUnsafe() method exposed above does some tricks to access a private field in the Unsafe class, because Unsafe.getUnsafe() can only be called by classes loaded by the bootstrap ClassLoader.
See also the article Avoiding Checked Exceptions by Don Schwarz.
Method #2: the Thread.stop(Exception)
public class ThreadStopExample { @SuppressWarnings("deprecation") public void methodWithNoDeclaredExceptions( ) { Thread.currentThread().stop(new Exception( "this should be checked" )); } public static void main( String[] args ) { new ThreadStopExample().methodWithNoDeclaredExceptions(); } }
This uses a deprecated method, but works. No portability issue, until the Java specification guys decide to remove the method.
It could have some side effects on the current thread as we are calling stop(). I'm not sure.
Method #3: using Class.newInstance()
Look at the signature of java.lang.Class.newInstance() and compare it to Constructor.newInstance()
public final class Class ... { public T newInstance() throws InstantiationException, IllegalAccessException } public final class Constructor ... { public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException }
You see it? no InvocationTargetException!
If you call SomeObject.class.newInstance() and the constructor throws an exception, the exception doesn't get wrapped into the InvocationTargetException (that is a checked exception).
So you can write an utility class like this, to throw checked exceptions without needing to declare them on the method signature.
public class Exceptions { private static Throwable throwable; private Exceptions() throws Throwable { throw throwable; } public static synchronized void spit(Throwable throwable) { Exceptions.throwable = throwable; try { Exceptions.class.newInstance(); } catch(InstantiationException e) { } catch(IllegalAccessException e) { } finally { Exceptions.throwable = null; } } } public class TestExceptionSpit { public static void main(String[] args) { Exceptions.spit(new Exception( "this should be checked" )); } }
Internally the Class.newInstance() uses the sun.misc.Unsafe class, but in this case this technique is fully portable because you are not using any deprecated or internal method. In fact it works also with GCJ JVM.
I tried to remove the synchronization stuff and the static field using an inner class, but it seems that the compiler does some strange trick translating the empty constructor in something else preventing Class.newInstace() to be used on that inner class.
The behavior of the Class.newInstance() is also documented:
"Note that this method propagates any exception thrown by the nullary
constructor, including a checked exception. Use of this method
effectively bypasses the compile-time exception checking that would
otherwise be performed by the compiler."
So your code is fully safe and compliant to the rules :)
Method #4: the sun.corba.Bridge
import java.rmi.RemoteException; public class Bridge { public void methodWithNoDeclaredExceptions( ) { sun.corba.Bridge.get().throwException(new RemoteException("bang!")); } public static void main( String[] args ) { new Bridge().methodWithNoDeclaredExceptions(); } }
This is more or less the same as using the Unsafe.class. The difference is that in this case you don't need to do the reflection stuff to access the private field "theUnsafe", because the Bridge class is doing that for you. Still using an internal JRE class with same portability issues.
Method #5: Generics
The following example takes advantage of the fact that the compiler does not type check generics...
import java.rmi.RemoteException; class Thrower { public static void spit(final Throwable exception) { class EvilThrower<T extends Throwable> { @SuppressWarnings("unchecked") private void sneakyThrow(Throwable exception) throws T { throw (T) exception; } } new EvilThrower().sneakyThrow(exception); } } public class ThrowerSample { public static void main( String[] args ) { Thrower.spit(new RemoteException("go unchecked!")); } }
Credits to "Harald" that posted a comment on Johannes Brodwall's blog.
I personally think this last one is the best solution: it uses a feature of the compiler against itself.
Conclusions
I think that having checked exception in Java is better than not having it. I already expressed why I am in favor of checked exceptions here. It's a design decision, you can choose to make your exceptions checked or unchecked, if you want to force your client to handle them or not; you can't do that on .NET, where checked exceptions simply do not exist.
Sometimes you have (or you have to write) methods throwing java.lang.Exception, and you get into the trap. So you may like to know that there is a dirty escape, and you can decide to use it or not... we saw that Sun is throwing undeclared checked exceptions in Class.newInstace(), ask yourself: if this is good for the JRE code, could it be good also for yours?
Usually you can wrap checked exception into RuntimeExceptions but this doesn't simplify the client code, because the caller in case of needing has to catch the RuntimeException, unwrap the cause and deal with it. Maybe a new Java keyword to throw checked exception without requiring the caller to handle them could help: I recommend reading post on Ricky Clarkson's about checked exceptions.
Finally I come to the decision to not use those tricks in my object doing the retry logic, and keep the messy catch logic on the caller code. In case of needing I will evaluate to use a Dynamic Proxy doing the retry logic and keeping its behavior transparent to the client.
To those who wants unchecked exceptions in Java... well, there is the way to have it: the example with Generics is a clean way to have it. Use it if you want, at your own risk.
Personally I would choose to use libraries with checked exceptions...
Other related articles
Friday Free Stuff by Chris Nokleberg, uses bytecode manipulation.
Don't Try This at Home by Bob Lee, exposes some methods also covered above.
From: http://en.newinstance.it/2008/11/17/throwing-undeclared-checked-exceptions/
Opinions expressed by DZone contributors are their own.
Comments