Avoiding Checked Exceptions

Compile-time checking of exceptions is a useful language feature, but
often this feature can become a nuisance. This is particularly true
when checked exceptions are used (in my opinion) inappropriately, for example to
signify an unavailable resource or a coding error.

When calling a method that throws a checked exception, you have a few
options. You can catch the exception and handle it yourself; however,
this is not always possible. You can declare that you also
throw the checked exception, but this can lead to a proliferation
of throws declarations as exceptions need to propagate
farther and farther up the stack. Many exceptions are best handled at the
top level of the stack, or even outside of the stack in anUncaughtExceptionHandler, and it doesn't take many checked
exceptions before this process can become untenable.

Another popular option is to convert checked exceptions into unchecked exceptions
by using a wrapper exception. For example:

Chaining exceptions in this manner has a few advantages.Exception.printStackTrace() will print the entire chain of stack
traces, so no information is lost in the wrapping process. Each
exception can also annotate the stack trace with additional debugging
information (for example, by appending the object identifier in the
above example).

There is one major disadvantage, though. We've preserved the
debugging information contained in the original exception, but we've
lost the ability to easily trap exceptions based on this information.
For example, let's assume that the following method is near the top of
the stack. This will no longer work, as the exception thrown is now
a RuntimeException rather than an SQLException.

However, there is a fourth option. We can actually throw a checked
exception at run-time without either the compiler or the bytecode
verifier knowing about it. The public class sun.misc.Unsafe
provides a wide variety of "dangerous" utility functions, including
direct memory access (which can be used to simulate pointer arithmetic)
and object access which is much faster than reflection. In subsequent
posts I will discuss many of these other features, but in this post I am
focusing on the throwException method.

The Unsafe class and throwException method are both public, so
any security surrounding this class are based entirely on whether you can
obtain a instance of it.
Code that is distributed along with Java can simply call the static factory
method Unsafe.getUnsafe(), but we're not quite so lucky. This
method verifies that the calling class was loaded through the bootstrapClassLoader, so we would have to modify the -Xbootclasspath
for this to work for our own code. However, there is another option -- if
the current SecurityManager allows it, we can override the accessibility
of the static field that stores the Unsafe instance and retrieve it
directly.

"Why does such a method even exist?", you may be asking yourself. Surely
Sun wouldn't throw any checked exceptions from a method that doesn't
declare those exceptions, right? That's cheating!

Well, in fact, they do -- in Class.newInstance(). If you get
a java.lang.reflect.Constructor and call newInstance()
on it, you will receive one
of three exceptions: IllegalArgumentException, IllegalAccessException,
or InvocationTargetException. The first two exceptions are caused by
the act of invoking a method through reflection, but the third is used
to wrap any other exceptions which are thrown from within the constructor.

However, Class.newInstance() behaves differently; it will not throw anInvocationTargetException. It may throw an IllegalArgumentException
or IllegalAccessException, but if
any exceptions are thrown from the constructor they will be thrown directly,
rather than being wrapped with an InvocationTargetException.

In fact, if you look at the code for Class.newInstance(), you'll see this:

I'm not necessarily advocating the use of this method to avoid compile-time
checking of exceptions, but I think it's important to know that you have
this option. It's a bit of a hack, but hey -- if it's good enough for Sun
it must be good enough for us, right?

There is one place in particular where I can see this method being useful.
Some Aspect Oriented Programming (AOP) frameworks, such as AspectJ, have
support for automatically "softening" exceptions by wrapping them with an
unchecked exception. However, this suffers from the same problem that
we highlighted above -- the identity of the exception changes after it
is wrapped, and therefore it is more difficult to catch the exception at
a higher level. It may be interesting to give these frameworks
support for using Unsafe.throwException instead.

Another interesting idea is to implement an annotation that softens
exceptions in this manner. Using either source-code instrumentation
(via apt) or bytecode instrumentation (as I described
in myPeeking Inside the Box
articles), code could be inserted that would useUnsafe.throwException to soften the exceptions. Unlike the AOP
approach, this would make it clear from the source code what is happening.

Have checked exceptions done more harm than good in your development career? If so, how do you deal with it?

10 Comments

tom_davies
2004-09-12 20:14:17

Semantics of higher level exceptions
I usually find that when I want to catch particular types of exceptions at a higher level in my code, the meanings of the exceptions are not related to the type of exception originally thrown.

For instance, I often divide exceptions in 'business' exceptions, caused by data a user has entered (and fixable by the user after they have read an error message) and 'application' errors, which are caused by program defects or infrastructure issues.

An SQLException caused by trying to insert a duplicate key might be a business exception, but one caused by failure to get a DB connection would be an application exception.

So wrapping and rethrowing the SQL exceptions allows me to categorise the exception to fit in with my higher level exception scheme.

It's also a place to add human readable context to the error.

Tom

robilad
2004-09-13 02:36:01

Please do not propagat the use of non-standard, undocumented APIs
As you may know, Sun changes the internals of their implementation every now and then, for a lot of good reasons. So such 'clever' hacks only increase the maintainance costs in the long run. I hope that you can see that there is a very good reason why Sun recommends staying away from sun.* APIs.

I also hope that you have checked the license of the code you posted. To my knowledge, Sun's SCSL does not allow distribution of modified source code, and it could be argued that your snippet is modified SCSL covered code. I'm not sure if a license violation automatically terminates your right to use SCSL covered code, but it may be in your best interest to check.

cheers,
dalibor topic

schwardo
2004-09-13 04:54:10

Please do not propagat the use of non-standard, undocumented APIs
Please don't misunderstand me. By no means am I advocating the use of the Unsafe API, or any other Sun-specific code, in a production environment. Obviously if compatibility across JVM versions or across vender implementations is important to you (as it is to most of us), this would not be an option.

As I said, the only semi-legitimate use of this that I can think of is as optional functionality in a framework that currently uses wrapper exceptions.

As for SCSL source code, the only source code other than my own which I posted was from Class.newInstance0(), which is distributed with the standard Java VM in src.zip. I did not modify this code in any way (other than excerpting it), and I am confident that there is no license violation in the way I have used this code.

g-rayman
2004-09-13 09:04:16

OK, we can throw it, but how can we catch it?
If a checked exception "cannot" be thrown in a try block, then the compiler not allow catching the exception in the catch block.

The compiler will complain that the catch block is unreachable.

robilad
2004-09-14 02:50:30

Please do not propagat the use of non-standard, undocumented APIs
Thanks for the quick reply, and sorry for the misunderstanding. I've just seen too much code that blindly imports sun.who.cares.if.it.breaks.Base64 and friends, I guess, all over an awful lot of code that's (apparently) used in production environents[1].

I'd say that any code that imports sun.* needs to be refactored. Relying on code with unknown side effects in a production environment sounds like a bad idea to me.

SCSL is a rather complicated software license, as software licenses go. It would be nice if Sun had a FAQ describing precisely what sort of common uses of SCSL covered source code Sun considers legal or illegal. That would help avoid such PR desasters like the JDocs thing a few weeks ago, where Sun's interpretation of their licenses and Javalobby's were quite different.

Well, at least it seems that Sun will be setting the fees for commercial use of SCSL sources to 0 USD for Java 5, so if you gained commercially in any way from posting SCSL-covered Java 5 source on your blog, you wouldn't have to pay Sun royalties any more.[3] :)

cheers.
dalibor topic

[1] JBoss, JOnAS, Ant, OpenOffice, ...[2]
[2] Ant and JBoss are cleaning up their act, gradually, though.
[3] Check out the commercial use sections of SCSL. Great read, though a little scary sometimes. :)

insanity_reigns_supreme
2006-04-22 10:57:45

This was possible even before Java 1.5...
Although the author uses the Unsafe facility in Java 1.4+ and goes to some trouble acquiring an instance of it, the capability to freely throw unchecked exceptions at arbitrary points in Java code has existed since Java 1.x and it can be implemented without resorting to any deprecated or sun.* code. (This capability is allowed by the JVM specification.)

(The EvilThrow class in the abovementioned article will throw any Throwable without requiring any compile-time checks.)

GeorgyBolyuba
2006-05-24 20:43:40

Misprint?
I didn't get this one:
However, Class.newInstance() behaves differently; it will not throw an InvocationTargetException. It may throw an IllegalArgumentException or IllegalAccessException, ...

But in code we see:private Object newInstance0()
throws InstantiationException, IllegalAccessException

It looks like misprint. Am I right?

yahoo
2006-08-09 01:12:07

check yahoo

2006-12-03 06:22:51

Jim
2007-10-18 07:28:35

ok check google

Sign up today to receive special discounts, product alerts, and news from O'Reilly.