Wednesday, April 21, 2010

Effective Exception Handling is Covered Effectively in Effective Java

I recently wrote that the chapter in Effective Java devoted to exception handling has been one of the most influential software development chapters I have read. The value of this chapter was reinforced by Elliotte Rusty Harold's blog post Bruce Eckel is Wrong. Harold's main assertion is that Eckel was wrong to write that checked exceptions must be caught at time of encounter because they don't necessarily need to be caught if they are declared as part of the method's throws clause.

What Harold wrote about checked exceptions is true, but it is really not all that much nicer to have to declare throws clauses on a series of methods when nothing really useful is going to be done with the exception anyway. While it's true that Eckel should have written that checked exceptions do not need to be caught if they are declared in the throws clause (and Eckel acknowledges this in the comments section), that omission does not really take away from the fact that any "handling" of checked exceptions does add verbosity to the code whether that "handling" is a catch or a throws clause. If something helpful can be done because of this extra code, it is justified. If nothing helpful can be done, the extra code is clutter. It's tedious and unnecessary ceremony.

This post has reiterated in my mind the value of the Effective Java chapter on exception handling. That chapter succinctly and authoritatively discusses effective Java exception handling. The Second Edition includes two chapters of particular relevance here: Item 58 ("Use checked exceptions for recoverable conditions and runtime exceptions for programming errors") and Item 59 ("Avoid unnecessary use of checked exceptions"). These chapters are worth the quick read, but even their titles are telling.

A mix of checked and unchecked exceptions is "effective," but checked exceptions should only be used for "recoverable conditions" and should not be used "unnecessarily." It is my experience that many more exceptions are nonrecoverable than recoverable. If I cannot do anything reasonable with an exception other than log it and quit, there's no reason to force every developer using that API to write a catch block or multiple throws clauses on all calling methods.

The use of checked exceptions only for recoverable conditions has become generally accepted in the Java community not because of laziness (at least not in all cases), but because of practical experience wasting time writing unnecessary code for situations in which nothing particularly useful could be done anyway. I'll just cite one example here. The Spring Framework made JDBC far easier to use in part by providing a richer hierarchy of runtime exceptions that was much easier to use than the single, checked SQLException. See my article Add Some Spring to Your Oracle JDBC for additional information on this.

The Java Tutorial trail entry Unchecked Exceptions - The Controversy enters this debate:

Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don't want to be bothered with specifying the exceptions your methods can throw. Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.


It should be noted that Harold also agrees that "Bruce is absolutely right that you should not catch an exception unless you know what to do with it."

One of the interesting facets of the Groovy language is that all exceptions are treated as runtime exceptions. I do sometimes like to use checked exceptions or explicitly catch checked exceptions because I can actually do something about them, but I admit welcoming the ability to choose for myself when to check and when not to check.

Harold stated that he wrote the post with the charged title because "it’s time to lay [Eckel's] misconceptions to rest once and for all." In my opinion, Eckel's omission of the ability to add a throws clause instead of catching a checked exception at first encounter is not as significant as made out in this post. More importantly, though, this is a validation of my earlier assertion that the Effective Java chapter on exception handling should be the definitive source of information on exception handling for Java developers.

1 comment:

jenetics said...

I agree to use runtime exceptions for programming errors. But it is hard to decide, as an API designer, whether the API user can recover from an error state and therefor use an checked exception. I'm using checked exception for conditions I can't control. The IOException is a perfect example. Whether you can recover from an IOException totally depends on your situation. Maybe you can use default properties if loading a property file fails. If not, you have to release used resources and shut down the application. Using an runtime exception in such cases only hides the problem.