The Artima Developer Community
Sponsored Link

Java Buzz Forum
Bruce Eckel is Wrong

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Elliotte Rusty Harold

Posts: 1573
Nickname: elharo
Registered: Apr, 2003

Elliotte Rusty Harold is an author, developer, and general kibitzer.
Bruce Eckel is Wrong Posted: Apr 20, 2010 4:12 AM
Reply to this message Reply

This post originated from an RSS feed registered with Java Buzz by Elliotte Rusty Harold.
Original Post: Bruce Eckel is Wrong
Feed Title: The Cafes
Feed URL: http://cafe.elharo.com/feed/atom/?
Feed Description: Longer than a blog; shorter than a book
Latest Java Buzz Posts
Latest Java Buzz Posts by Elliotte Rusty Harold
Latest Posts From The Cafes

Advertisement

Every time the subject of checked versus runtime exceptions comes up, someone cites Bruce Eckel as an argument by authority. This is unfortunate, because, as much as I like and respect Bruce, he is out to sea on this one. Nor is it merely a matter of opinion. In this case, Bruce is factually incorrect. He believes things about checked exceptions that just aren’t true; and I think it’s time to lay his misconceptions to rest once and for all.

Let’s see exactly what Bruce’s mistake is. The following is an extended selection from Thinking in Java, 4th edition, pp. 490-491:

An exception-handling system is a trapdoor that allows your program to abandon execution of the normal sequence of statements. The trapdoors used when an “exceptional condition” occurs, such that normal execution is no longer possible or desirable. Exceptions represent conditions that the current method is unable to handle. The reason exception-handling systems were developed is because the approach of dealing with each possible error condition produced by each function call was too onerous, and programmers simply weren’t doing it. As a result, they were ignoring the errors. It’s worth observing that the issue of programmer convenience in handling errors was a prime motivation for exceptions in the first place.

One of the important guidelines in exception handling is “Don’t catch an exception unless you know what to do with it.” In fact, one of the important goals of exception handling is to move the error-handling code away from the point where the errors occur. This allows you to focus on what you want to accomplish in one section of your code, and how you’re going to deal with problems in a distinct separate section of your code. As a result, your mainline code is not cluttered with error-handling logic, and it’s much easier to understand and maintain. Exception handling also tends to reduce the amount of error-handling code, by allowing one handler to deal with many error sites.

Checked exceptions complicate the scenario a bit, because they force you to add catch clauses in places where you may not be ready to handle an error. This results in the “harmful if swallowed” problem:

try {
// ... to do something useful
} catch (ObligatoryException e) {} // Gulp!

Do you see the mistake? It’s a common one. Let me repeat it, so it’s really obvious [emphasis mine]:

Checked exceptions complicate the scenario a bit, because they force you to add catch clauses in places where you may not be ready to handle an error.

This is false. You are never forced to add catch clauses where you are not ready to handle an error. This isn’t a matter of opinion. It’s a matter of fact. Checked exceptions do not require catch blocks. They require a catch block OR a throws declaration. Eckel’s entire argument is based on ignoring the possibility of a throws declaration.

While Bruce is absolutely right that you should not catch an exception unless you know what to do with it, this in no way means that you should insert a catch block everywhere a checked exception may be thrown. If you aren’t ready to handle an error at one place, let the exception bubble up. If a checked exception is thrown inside a method where you are not ready to handle it, then the correct response is to add a throws clause to the method indicating that the exception will bubble up from that method. For example,

 public void doSomethingUseful() throws ObligatoryException {
   // ... do something useful that throws an obligatory exception
} 

You do not and should not insert a catch block in a method where you cannot do anything reasonable in the catch block. Checked exceptions never meant that every exception had to be caught as soon as it was thrown. It is perfectly acceptable to declare that a method throws a checked exception. Indeed, this is exactly how exceptions are meant to be used. It warns whoever calls your method that they need to be ready for this exceptional condition, and they either need to catch it and handle it themselves; or, they themselves need to declare that they throw it so that they warn their callers.

Yes, if it were true that every checked exception needed to be caught immediately, then checked exceptions would be incredibly inconvenient. However, experienced Java programmers don’t do this. Catching each and every checked exception at the first opportunity is a sure mark of a novice Java developer.

Occasionally, you’ll override a method inherited from a superclass or implement a method declared in interface that does not declare it throws the checked exception that your method throws and thus can’t throw the correct exception. In this case alone, it may be acceptable to wrap the exception in either a checked exception that the original declaration declares or in a runtime exception (if the original declaration does not declare any appropriate checked exceptions). For example,

   @Override
    public void doSomethingUseful() {
        try {
     // ... do something useful that throws an obligatory exception
        }
        catch (ObligatoryException e) {
            throw new ObligatoryRuntimeException (e);
        }

However, you still don’t need to handle the exception before you’re ready for it.

I will note that this situation is a failure of design. When you’re forced to do this, one of two things is broken:

  1. The superclass/interface was not designed properly for extension. Specifically it did not take into account the exceptions overriders/implementers might reasonably want to throw. The method likely should have been declared final and probably shouldn’t be extended at all.
  2. The overriding/implementing method is violating the contract of the method it overrides/implements by doing something it really should not be doing.

A good example of the latter would be letting an IOException wrapped in a RuntimeException escape from the run() method of java.lang.Thread or java.lang.Runnable. These methods do not have sufficient context to handle such an exception, but something further down in the call chain does. The exception should be handled before it bubbles all the way up (though not necessarily in the same method where it’s first thrown).

In a method properly designed for extension, an empty throws clause (or a throws clause that does not match the actual exception) indicates that callers of that method cannot handle and do not expect such an exception. An overriding method that throws a new exception is violating the contract by failing to handle it, the same as it would were it to restrict a precondition or loosen a postcondition. (In essence, this is another form of loosening a postcondition.)

Eckel is not the only one to make the mistake of assuming that checked exceptions must be immediately at handled at the first possible opportunity. For example, he cites Barbara Liskov and Alan Snyder explaining their decision not to include checked exceptions in CLU:

requiring that the text of a handler be attached to the invocation that raises the exception would lead to unreadable programs in which expressions were broken up with handlers

True. Requiring that the text of a handler be attached to the invocation that raises the exception would lead to unreadable programs. Fortunately neither Java nor checked exceptions require any such thing. When handlers are appropriate they can usually be moved to the end of a method, far away from the the invocation that raises the exception. When handlers are inappropriate, you use a throws clause instead. Immediately following every statement that can throw an exception with a catch block is bad form on a par with goto.

Liskov and Snyder continue:

“We felt it was unrealistic to require the programmer to provide handlers in situations where no meaningful action can be taken.”

Also very true, but of course, checked exceptions do not require the programmer to provide handlers in situations where no meaningful action can be taken. When no meaningful action can be taken (out of memory error, stack overflow, class not found, etc.) Java programs throw Errors, not checked exceptions, not even runtime exceptions. Checked exceptions signal environmental problems that programmers cannot prevent or predict, should test for, and most decidedly can handle. To choose the most extreme example, if a production-worthy database system is writing a file and the disk fills up, it should handle the condition gracefully without corrupting the database. A disk full error is neither unforeseeable nor unmanageable. Most checked exceptions aren’t even that tricky to respond to.

What checked exceptions actually do require is that any method that can throw a checked exception warn its callers that the exception may be thrown. That’s all. A checked exception is nothing more and nothing less than part of the return type. Methods may return normally or they may throw exceptions. It makes just as much sense to specify the exceptions that can be thrown by a method as it does to specify that a method returns an int or a String. A method that does not declare the exceptions it throws is incomplete.

A lot of us didn’t really get checked exceptions when Java was released in the mid-nineties. It was a genuinely new idea that I don’t think any programming language before had foreshadowed. Liskov and Snyder wrote the paper quoted here in 1979, and their quotes make sense if you assume they simply didn’t conceive of having different kinds of exceptions in the language. if you only have one kind of exception then it makes sense for it to be a runtime exception rather than a checked exception.

Personally, I didn’t really understand how to use exceptions until the first edition of Effective Java was published. But it’s been 15 years. That’s long enough for the message to get out. In 2010 we know better. Proper error handling requires distinguishing programming errors (runtime exceptions) from environmental problems (checked exceptions). Proper error handling requires correcting programming errors and writing handlers for unpreventable environmental conditions. Proper error handling requires knowing when to catch and knowing when to throw. If you try to work with only one kind of exception, or try to get by with only catch but no throws, then exceptions are going to seem very ugly and inconvenient. But if you use checked and runtime exceptions, and use catch and throws, then your error handling code will be far cleaner, safer, and more maintainable. Error handling without checked exceptions and throws is like arithmetic without * and /. Sure, you can do it, but why would when you when using the features the language offers makes life so much simpler?


Read: Bruce Eckel is Wrong

Topic: Microsoft to investigate reports of poor working conditions at factory in China Previous Topic   Next Topic Topic: Toyota conducts safety tests on all SUVs after Consumer Reports `Don't Buy' warning on Lexus

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use