Saturday, 6 September 2008

(I can't get no) Satisfaction



I can't get no satisfaction, I can't get no satisfaction
'Cause I try and I try and I try and I try

(I Can't Get No) Satisfaction - The Rolling Stones


Checked vs. Unchecked Exceptions


It's a controversial subject; searching Google for checked versus unchecked exceptions reveals a lot of heated discussions both for and against Checked Exceptions.
Charles Lowell has a particularly succinct (and possibly NSFW) view

My view is similar. I can almost see a situation where I would like to ensure that exceptions are dealt with in a certain way, but it doesn't quite reach as far as using checked exceptions.

Noisy IDEs

I think my issue is largely to do with the way that the IDEs (and compilers) deal with checked exceptions.
In Eclipse, if a method throws a checked exception, then auto-fix suggests two options; "Surround with try/catch" and "Add throws declaration to method"
I particularly have a problem with the second one. The further you get away from the source of the exception, the less likely you are to deal with it in a useful way. Not dealing with the exception is a reasonable option, but why does everyone in the call chain need to know about an exception that someone in the lower levels didn't care enough about to handle?
Using unchecked exceptions gives us the choice to handle or propagate the exception, without polluting the higher levels with knowledge of unhandled exceptions.

The default Eclipse template for try / catch is to catch the exception and print the stack trace. This isn't really handling the exception, it's ignoring it (even if we've logged it). There is no way to know if this is an acceptable default without understanding the underlying code. Unfortunately, if we are leaving it as the default we probably don't understand the underlying code (yet).

An alternative to Checked Exceptions
One of the nice things about checked exceptions is that they let you know the types of exception you can get. You get the IDE auto-completion and compiler checking.
A checked exception is essentially saying, "You might get this exception, and I insist that you deal with it in the way that you feel is most appropriate"
How can we continue to do this, and at the same time prevent boiler-plate or unnecessary pollution?

Something that I'm thinking about (but haven't had the need to implement yet), is the concept of an ExceptionHandler class. This is an ideal place for Java Generics to come into play.
For example:

public void someCodeThatMayThrowAFileNotFoundException(String fileName, ExceptionHandler<FileNotFoundException> exceptionHandler) {
try {
open(fileName); // throws FileNotFoundException
}
catch (FileNotFoundException e) {
exceptionHandler.handleException(e);
}
}


By coding in this way, we can provide sensible default actions, such as:


// The Generic stuff has not been checked and is for illustration purposes.
// Let me know if I need to correct it :-)
public class PropagatingExceptionHandler extends ExceptionHandler<T extends Exception> {
public void handleException(T exception) {
throw(exception);
}
}

public class LogAndIgnoreExceptionHandler extends ExceptionHandler<T extends Exception> {
public void handleException(T exception) {
exception.printStackTrace(); // or log4j equivalent
}
}


This reduces the try / catch noise, gives us the compiler checking and type completion, and explicitly details the strategy that we are using for handling the exceptions for this part of code.

I'm open to someone showing me a really good use of checked exceptions, but for the time being, I won't be using them.