Exceptions: checked and unchecked

In the Java community there's a still this disagreement about whether to use statically checked exceptions versus their unchecked opponent. In this article I provide some facts about exceptions and my personal view on using them.

Exceptions

An exception is an event that disrupts the normal flow of execution in a program.

Nowadays almost any major programming language support the raising and handling of exceptions. These languages allow for registering an exception handler (i.e. a catch/rescue block). If an exception is raised the first exception handler up the call stack is invoked.

Because handling an exception always was optional, OCaml introduced exception checkers. This was an external tool that could be used during compilation to fail when no handler for a particular exception was registered.

Growing on this idea compile-time checks upon exception handling were added to CLU and Modula-3. These languages check for each exception that is listed in the throws declaration of a function whether a handler is registered up the call stack. Any exception not listed was not checked at compilation time. C++ has a likewise feature known as exception specifications.

Checked exceptions

Then checked exceptions as we know them today were added in the first version of Java. The difference with earlier attempts is that Java decides to check an exception based on its class hierarchy, while others decide based on function signatures. Therefore a checked exception is an exception that is checked on compile time. A function that might raise it must declare it in its signature. A calling function must either register a handler or declare the exception it its signature as well.

At the time of introduction the strictness of checked exceptions seemed like a good idea. By having to list all the exceptions that are possibly raised in a functions signature, you could never lack a handler.

The problem with checked exception wasn't visible until late after a project entered maintenance. (Remember, at the time agile wasn't a big thing and most projects were done waterfall style.) As it turned out any change to a function that might raise a checked exception forces its callers to change too (possibly multiple layers of them). And that's a straight violation of the open-closed principle [OCP]. Micheal Feathers provides more details on this in an excellent chapter on error handling in Clean Code.

Because of this checked exceptions are a bad idea. After Java no major language adopted the concept (not even C#). And a few years ago C++ deprecated exception specifications in its recent C++11 standard.

Express intent

As unchecked exceptions don't violate the OCP, does that mean they should always be used over checked exceptions? In Effective Java Joshua Bloch says that if you can reasonably expect your client to recover from the exception, you should consider using a checked exception. Hence any client is forced to deal with the exception.

Although this might be considered a good reason to use checked exceptions (especially at the time this great book was written), personally I see their use more as a design smell.

As Bertrand Meyer writes in his book Object-Oriented Software Construction there are only two meaningful ways that a routine can react when an exception occurs: by failure or by retry.

Using unchecked exceptions a function automatically handles a failure. The failure then is normally passed to some more general handler up in the call stack. Practically this handler logs the failure and display a friendly message to the user.

If a function should opt a retry (in which case you might doubt to use a checked exception) it would help if this was expressed somehow different from expecting a failure reaction. An exception (checked or unchecked) won't express this intention quite well.

In such case I want to communicate the event through the API. Adding a dependency to the target object (whose member function must communicate the error) helps me reason about what the error means in term of the caller. By doing this I can descriptively name the relationship between the caller and the target, and make it explicit that someone must handle the error.

I hope this might be of help to you and provided you some new insights.

0 reacties:

Post a Comment