Why you should love checked exceptions

Created: 2013/07/06 14:18:25+0000

This week at work I encountered a NullPointerException caused by returning a null value. The return value was not tested and when it was used it threw a NullPointerException. This of course had to happen in patch of bad exception handling.

First the bad exception handling. This is what I had to work around first. A method was throwing a couple of checked exceptions with no shared super class before Exception. Some programmer decided to say that it threw Exceptions and the calling method happily caught every Exception thrown. It logged the message but not the stacktrace. Below is some simplified code to show you what I had to deal with:

public void exceptionHandlingMethod()
{
    try
    {
        exceptionThrowingMethod();
    }
    catch(Exception e)
    {
        System.err.println(e.getMessage());
    }
}

public void exceptionThrowingMethod() throws Exception
{
    ...
}

So the exception throwing method throws two types of checked exception to be handled further up the call stack. It happened to be throwing a NullPointerException as well which was caught and reported with the unhelpful exception 'null'. This bad exception handling was always likely to cause problems. It did not take me long to refactor this exception handling code to something like this:

public void exceptionHandlingMethod()
{
    try
    {
        exceptionThrowingMethod();
    }
    catch(AException | BException e)
    {
        System.err.println(e.getMessage());
    }
}

public void exceptionThrowingMethod() throws AException, BException
{
    ...
}

This stopped catching the null pointer exception and allowed me to get a useful stacktrace. When writing exception handling code you should catch only the exceptions you expect.

Now the NullPointerException. A String was being returned and a regular expression applied to it. The a null reference was returned because it was not available, it went unchecked and when the regular expression was applied the NullPointerException was thrown. Like this:

    Pattern pattern = Pattern.compile(patternString);
    String string = getString();
    Matcher matcher = pattern.matcher(string);

I put in a test for the null reference like this:

    Pattern pattern = Pattern.compile(patternString);
    String string = getString();
    if (string == null)
    {
        ...
        return;
    }
    Matcher matcher = pattern.matcher(string);

and for work I was done. There is a better solution to this. Good exception handling would have prevented this bug ever happening. The getString() method returns a string it should not return nulls. Instead it can throw a checked exception if it cannot return a string. This prevents anyone using the method from leaving out code that is needed. Consider this code:

    Pattern pattern = Pattern.compile(patternString);
    try
    {
        String string = getString();
        Matcher matcher = pattern.matcher(string);
    }
    catch (NoValueException e)
    {
        ...
    }

Yes this is more verbose but it is also less prone to bugs. Where NoValueException is a checked exception thrown by getString() everywhere this method is used the exception must be handled. This prevents a lazy programmer leaving something out. Checked exceptions are supposed to be used for implementing extraordinary control flow. This can be used to force programmers to deal with corner cases. The method getString() should return a String, if it cannot it should not return a String, it should make the calling code jump to a different code path. Excuse me while I rewrite my code.

To summarise:

  • Don't catch exceptions you do not expect
  • Use checked exceptions to handle extraordinary control flow
  • Do not be afraid of more verbose code
  • Do not hate checked exceptions, feel the love