This morning, I was looking through some code and I was annoyed (yet again) at Java’s exception hierarchy, particularly when it comes to checked exceptions. Just as a reminder, everything that can be thrown in Java derives from Throwable
. The predefined direct subclasses are Error
and Exception
. (You can derive from Throwable
directly yourself, but I’ve never seen anyone do it, thank goodness.) Exception
, and any class deriving from it, count as a checked exception – one that you have to declare if your method might throw it. Oh, except for RuntimeException
, and its descendants such as NullPointerException
. Blech.
This is really painful in some situations. In particular, the code I was looking at wanted to catch everything, act on it, and then rethrow it. My method was declared to throw IOException
, and without the catch
block everything compiled fine, so I knew that nothing I was calling should throw any checked exceptions other than IOException. However, rethrowing the exception is tricky – because the compiler doesn’t know what you’re up to. I ended up with this foul code:
try
{
}
catch (Throwable t)
{
if (t instanceof IOException)
{
throw (IOException) t;
}
if (t instanceof RuntimeException)
{
throw (RuntimeException) t;
}
if (t instanceof Error)
{
throw (Error) t;
}
throw new RuntimeException(t);
}
finally
{
}
|
Nasty, isn’t it? It would be lovely to somehow tell the compiler that you know there won’t be any other kinds of checked exceptions thrown, just rethrow the original, it’s all right guv, you can trust me, honest.
Well, apparently you can’t really trust me. Not since the hack I worked out this morning. You see, exception checking only occurs at compile time. So, let’s define a really harmless little class called ExceptionHelper
:
public class ExceptionHelper
{
private ExceptionHelper()
{
}
public static void rethrow (Throwable t)
{
}
}
|
Nothing nasty going on, is there? So the compiler won’t mind at all if I change the original code to:
try
{
}
catch (Throwable t)
{
ExceptionHelper.rethrow(t);
}
finally
{
}
|
The only trouble is, it doesn’t rethrow the exception any more, regardless of the name of the method. But as I suspect you’ve guessed by now, once we’ve satisfied the compiler, we can change ExceptionHelper.rethrow
slightly:
public static void rethrow (Throwable t) throws Throwable
{
throw t;
}
|
Recompile ExceptionHelper
but not the calling code and we achieve exactly what we want – it will rethrow whatever exception we ask it to, and we’ve fooled the compiler into not worrying about the potential consequences. Of course, this means we could change the code in the try
block to something which throws a completely different checked exception, and we’d never know until it happened – the compiler couldn’t help us. The workaround for this is to temporarily remove the catch
block and see whether or not the compiler complains.
I’m not actually suggesting anyone should do this, despite a certain appeal in terms of simpler, more readable code in the catch block. A hack like this is horrible, evil, awful. Which is why I had to share it, of course.