Wednesday, 22 December 2010

In Defence of Checked Exceptions

I'm often asked about exceptions in Java, and whether Unchecked Exceptions are "better" than Checked Exceptions.

This is probably because in our Spring Framework online course, I spend a while talking about Spring's Data Access Exception strategy, where checked exceptions are converted to unchecked ones.

See also blogs like Bruce Eckel's , which is fairly damning about Checked Exceptions.

As some of you may not be familiar with the difference between Checked and Unchecked exceptions, let's look at the difference between them.

Checked Exceptions

A checked exception is one that extends the java.lang.Exception class, and the rule is:

"Any call to a method that might throw a checked exception MUST handle the exception".

By "handle", I mean it must either

a) Handle the problem in a try...catch block
b) Throw the exception to the next level up

This means, that unless you're cheating by swallowing the exception somewhere (this means an empty catch block), or you're throwing exceptions from main(), then checked exceptions are guaranteed to be handled by your code, somewhere in the call chain.

Unchecked Exceptions
These are exceptions that don't need a try...catch block anywhere, although you can optionally try...catch them if you want to.

To define an unchecked exception, your exception definition will extend java.lang.RuntimeException.

The risk of using Unchecked Exceptions is that they might never get caught. If they don't, your program crashes. I'll talk about why unchecked exceptions are useful in the second part of this post.

Using Checked Exceptions

Surveying the Java blogging world (eg Bruce's post above), it's fair to say that Checked Exceptions have a terrible, terrible reputation and are hated by a large proportion of Java programmers. Whilst I don't blame them, this is unfair. Checked Exceptions have a lot of value but they are overused.

On my live Java course, I use a simple example of a checked exception...

public class NoOrdersAvailableException extends Exception
{
   // nothing important to add
}

Which is thrown from a method something like the following:

public class OrdersQueue
{
   public Order getNextOrderInQueue() throws NoOrdersAvailableException
   {
      // do a database query to find the order
      // omitted

      if (nextOrder == null) throw new NoOrdersAvailableException();
      return nextOrder;
   }
}

Why does the getNextOrderInQueue() method need to throw an exception? Because it has reached a condition where there are no orders available, and therefore it cannot fulfil the task it said it was going to do. I think of the signature of the method as being a contract:

public Order getNextOrderInQueue()

Meaning, "if you call this method, with no parameters, then the next order in the queue will be returned".

Notice that we cannot handle the problem in this method. There are no orders in the queue, and the only solution is for the user interface to report this back to the user. But this is not a user interface class, so the caller needs to handle the problem.

And there's the key: the caller NEEDS to handle this problem, and if the caller somehow misses the problem, we would have a serious problem (possibly silent failure).

Here's the caller:

public class UserInterface
{
   public void showNextOrder()
   {
      try
      {
         Order requiredOrder = ordersQueue.getNextOrderInQueue();
         // now add the order to the user interface screen
      }
      catch (NoOrdersAvaialableException e)
      {
         // show an information message to the user
      }
   }
}

I admit the code is long winded, and typing in try..catch blocks is never fun. But it is at least readable, I can work out exactly what is happening from this code with little prior knowledge (this is why naming exceptions well is a crucial skill).

Without checked exceptions, we would have to handle nulls, or create blank Order objects, or have some kind of pre-check such as "isOrderReady", which would be prone to race conditions (you get back true from isOrderReady, but by the time you've called getNextOrderInQueue(), some other process has stolen your order!).


So checked exceptions have a very valuable place in helping you to write readable, clean and safer code. But in my next blog post, I'll describe where Checked Exceptions should NOT be used.

2 comments:

  1. Interesting article.

    While it's useful for exceptions to be _declared_ by the functions you are calling, so you can know what to expect, forcing you to catch them at that point is less beneficial.

    Perhaps you could explain the benefit of throwing a NoOrdersAvailableException in your example instead of simply returning null. Is that really an exceptional condition in the sense that something unexpected has happened, or is it just a special case?

    In any event, I look forward to your follow-up article about unchecked exceptions.

    ReplyDelete
  2. I think you're right - it's not an exceptional conditional at all just a special case.

    You've hit the nail on the head. In my opinion, if the creators of Java had invented a new term for a Checked Exception, much of the confusion would never have arisen.

    And I agree that forcing you to handle it there and then is less beneficial (you might have to keep rethrowing all the way up the chain), but you can always add a throws declaration to the caller to automatically rethrow it.

    Re null: it reads better to be able to see that there is a special case that needs special handling (null wouldn't give any readable context), and with a null you can easily forget to handle the special case.

    Also, null can't convey any meaning other than "nothing" - if more than one "special case" is possible then of course we're in the horrific realm of return codes!

    That's not to say returning null doesn't have its place. Take the get() methods in the collections API - it would be a real pain to have to try...catch for NoElementFound exception when most of the time you know you're going to find an element, because you put the elements there in the first place.

    So I'm arguing that checkeds have their uses, but in very small doses, with care and precision.

    Having said all that, I don't seem to miss checked exceptions when working in Groovy or Ruby...

    ReplyDelete