I am Paul Wilson; Mere Complexities Limited, sells my consulting, coaching, and coding services. I am passionate about Agile, particularly Test Driven Development.


Taking exception to Checked Exceptions

A couple of years ago I changed my mind about Checked Exceptions. It was Rod Johnson's J2EE Design and Development that tipped me over to the other side, but I had been uneasy with them for a while: I had noticed that client code could rarely (if ever) do anything useful with supplier exceptions except log, wrap, and rethrow. My biggest criticism of checked exceptions is that they leak information, making polymorphism more difficult to apply. Simple example:
public interface UserNameHolder
{
  void addName(String name);
  boolean isUser(String name);
}

public class InMemoryUserNameHolder implements UserNameHolder
{
  private Collection _names = new ArrayList();
  public void addName(String name)
  {
    _names.add(name);
  }
  // Etc .....
}
Ok. No problems above. But
public class DatabaseUserNameHolder implements UserNameHolder
{
  public void addName(String name)
  {
    Connection connection = null;
    PreparedStatement stmt = null;
    try
    {
      connection = getConnection();
      stmt = connection.prepareStatement(
      "INSERT INTO nameholder (user_name)  VALUES(?)");
      stmt.setString(1, name);
      stmt.executeUpdate();
    }
    catch (SQLException e)
    {
      // Oh Oh - what now?
    }
    finally
    {
      closeNicely(stmt, connection);
    }
  }
  // etc etcc
}
Now we don't know what to do with our SQLException. Previously I would have added a generic UserNameHolderException; but that extra class would add no useful functionality. All the client can be reasonably expected to do is display a generic internal failure message. Now I use runtime exceptions; such exceptions are handled at the very bottom of the message loop and a generic failure message is displayed (actually, it is a tiny bit more sophisticated than that). My rules Currently I prefer the following rules Runtime Exceptions I tend to extend RuntimeException with UnrecoverableFailureException. This logs in the constructor (taking the throwing classes' logger as a parameter). This way the exception is logged only on throwing: clients know that they need not catch and log.
public UnrecoverableFailureException(Logger clientLogger, String msg, Throwable e)
{
  super(msg, e);
  clientLogger.error(msg, e);
}
UnrecoverableFailureExceptions indicate, in Design by Contract terms, postcondition failures. It says something like "Sorry, I couldn't store the name and there's nothing you can do about it.". Checked Exceptions Checked exceptions are best used to indicate uncheckable precondition failures. By uncheckable I mean preconditions that the client cannot be reasonably be expected to check before calling the method. For example we might have:
/**
* Add a user to the system
* PRECONDITION: !isUserNameAdded(userName)
*/
public synchronized void addUser(String userName, UserDetails details)
{
  if (isUserNameAdded(userName))
  {
    throw new IllegalArgumentException("'" + userName + "' has already been added");
  }
  // etc...
}
What if we are in a multithreaded environment? It would be difficult (unreasonable) to guarantee that a user has not been added between checking isUserNameAdded() and calling addUser. This is a case for a checked exception.
public synchronized void addUser(String userName, UserDetails details) throws UserHasAlreadyBeenAddedException
{
// etc...
}
Consequences
  • Cleaner code. My code is less littered with try, catch, wrap an throws.
  • Better encapsulation. I am no longer tempted to give away implementation details by propagating SQL\EJB\JNDI\etc.. exceptions to code that need not know.
  • Easier polymorphism. Implementing interfaces is much less painful. The command pattern, in particular, becomes easier to use.
  • More arguments. It is quite difficult to convince old-school Java Developers to use RuntimeExceptions. In my last team project I only managed to implement a general "unrecoverable" exceptions that was checked and nearly every method threw: as it was universally thrown it became a virtual runtime exception.
  • Requires a single "message" loop mechanism. These runtime exceptions do need to be caught in order to display a general failure message, and it would be bad if that code became littered througout the application. In servlet environments this is easy to achieve with the Front Controller pattern . I haven't tried (yet) in Swing.
Links

1 Comments:

Anonymous Anonymous said...

Interesting - I agree that checked exception are mostly rather useless The real worry is the abuse of Exception usage.

In my experience - Exceptions are thrown far too much in production code - rather Exceptions should be thrown only when the erroneous condition is a truly Unexpected erroneous condition.

More time and effort should be put into decided if an erroroneous condition warrants an Exception - or simply an error code.

11:37 AM  

Post a Comment

<< Home

subscribe here subscribe

About me

picture

Conference

Scotland on Rails Organiser

Previous blog posts

Blog archive

Other links: