Errors: Checked Exceptions

From Matt Morris Wiki
Jump to navigation Jump to search

Programming - Errors


Definitions

checked exceptions

"Checked exceptions" are exceptions that need to be explicitly dealt with as they arise, by one of the following methods:

   * handling the problem
   * adding the type of the exception to the function propagating it
   * (java only) rethrowing as an unchecked exception

They are, technically speaking, available in both Java and C++. However, only Java's checked exceptions are used in practice. This page will describe checked exceptions, and how best to use them.

c++ checked exceptions

C++ exception specifications can be dealt with very briefly, because they have such serious problems that there is unanimous agreement by generally accepted authorities on C++ usage that they should never be used! Major issues include the following:

   * violated specifications are only caught at runtime
   * interaction with templates is problematic
   * specifications are not allowed on typedefs

See Sutter's Guru Of The Week #82 and the boost library exception-specification rationale for more detailed arguments.

java checked exceptions

The Java debate on usage of checked exceptions is a lot more open; Brian Goetz has written a good starting point for covering the controversy. Here are some of the main positions taken:

   * Sun acknowledge that there is a controversy , but state that you should use a checked exception if a client can reasonably be expected to recover from a problem.
   * However, Bruce Eckel questions whether they are a good idea in any circumstances.
   * And Anders Hejlsberg, the lead C# architect, states that they didn't include checked exceptions because of scalability and robustness issues.
   * But, finally, James Gosling, Java's creator, riposts that checked exceptions improve robustness when properly used.

So, why do people have problems with Java checked exceptions, and should they in fact be thrown and/or propagated? First, let's look more closely at how checked exceptions relate to the other ways of dealing with errors that we've covered so far.

More On Java

the nature of java's checked exceptions

The main thing to bear in mind is that checked exceptions in Java have as much in common with return values as with unchecked exceptions. You could think of them as

          slow object-oriented return values that you have to handle

To expand on the above statement:

   * slow: like unchecked exceptions, they require an object to be created
   * object-oriented: like unchecked exceptions, they can be thrown from methods that don't have return values, such as constructors
   * return values: like return values, they must be dealt with manually: either by catching them, or by specifying them in the "throws" clause of the propagating function (known as the catch or specify requirement)
   * that you have to handle: unlike either return values or unchecked exceptions, they have to be handled or a compile-time error will result

Here's a side-by-side comparison of return values, unchecked exceptions and checked exceptions: Error Type Propagation Speed Constructors Can Use Must Handle? Return Value Manual Fast No No Unchecked Exception Automatic Slow Yes No Checked Exception Semi-Manual Slow Yes Yes

The table shows that checked exceptions offer gains and losses compared to either of the alternatives. Compared to unchecked exceptions, they lose automatic propagation, but gain compile-time checking. Compared to error codes, they lose speed, but are easier to propagate and gain both compile-time checking and the ability to be generated from object constructors.

checked exceptions may be good if you can handle the error right away

We start to see why Sun recommends checked exceptions for errors that clients are likely to be able to handle right away:

   * the work needed to propagate them stops being a problem
   * they'll keep their advantages of being usable in places where return values are not (just like unchecked exceptions) and of forcing the programmer to acknowledge the problem there and then (unlike unchecked exceptions)

Checked exceptions are, granted, slower than simple return values. But if speed isn't a genuine issue, checked exceptions are a very strong contender as a consistent way to signal errors that are likely to have a local fix available.

checked exceptions may be bad if you have to propagate them very far

Since checked exceptions are "explicit", like return values, they have to be handled in code. If they are passed up many levels, then the work involved in this may start to grate, even if they require less code to pass on than return values. At the very least, users will need to come up with some kind of rule for converting types, since otherwise the number of different exceptions referenced in a "throws" clause will increase without limit.

how best to propagate checked exceptions?

So, you've got a checked exception to handle. What do you do? If you're lucky, it may be obvious how to resolve the problem. If that is so, you can catch and handle the exception. But if you can't resolve the problem in this method, you have to propagate it outwards to some other method that can handle it. When propagating a checked exception, apart from just adding it to the "throws" clause of every method all the way up the call chain, here are two common strategies that people follow:

Wrap In An Unchecked Exception: you can pass the checked exception to the constructor of a RuntimeException (or whatever else you are using as your subsystem's base class for unchecked exceptions), and throw that instead. This effectively removes use of checked exceptions from your system - it is as if every exception in Java was an unchecked exception.

Convert Checked Exception Types: translate checked exception types as you pass through levels of abstraction, so you don't have more and more types to add to your method's "throw" clauses. In the easiest case, you map all checked exceptions to a single type for a given subsystem, so you have just one checked exception type per package or group of packages.

bad arguments about how widely to use checked exceptions

Sometimes people try to argue for or against Java's checked exceptions on the grounds of what's easiest for the programmer. It's very unclear that any firm conclusions can be drawn on these grounds though. Neither of the two ways of propagating exceptions above has to involve a large amount of work. Of course, if people refuse to see the need for checked exceptions at all, then wrapping them in RuntimeException instances does represent extra effort, but this only needs to be done once for any given method.

Sometimes people try to argue for or against Java's checked exceptions on the grounds that they dictate how the programmer handles errors - whether they believe this to be a good thing or not is another question. But we've seen above that it's very easy to carry on as if checked exceptions don't even exist if you wrap them in RuntimeException instances. It's hard to see where the element of compulsion lies.

Sometimes people argue against Java's checked exceptions on the grounds that they leak implementation details, possibly leading to a combinatorial explosion of the number of distinct exception types - since the types of the checked exceptions on a function's signature show what kinds of operation are being carried out several levels below. However, either of the two approaches to propagation above, whether wrapping or converting, will take care of this issue. No advocate of Java's checked exceptions would seriously suggest that an SQLException should be propagated, unchanged, all the way up to the main loop of an application. Exceptions need to be translated at different abstraction layers, as do any other type.

how widely should checked exceptions be used?

Checked exceptions offer a more universally usable alternative to return values for problems that may be recoverable from locally. Their only real disadvantage here is speed - you will still need to use return values if the cost of throwing and catching an exception is too high.

The case for checked exceptions becomes less clear-cut as the error information has to be propagated through the program. Whether to keep propagating error information in the form of checked exceptions comes down to a trade-off between:

   * how much you need to know about the exceptions as they are propagating, and
   * how much work you want to do to explicitly pass checked exceptions through your code.

So, if you don't care what type of exception is being thrown from a subsystem, you will want to wrap checked exceptions in unchecked exceptions as soon as possible, and make unchecked exceptions the default way of propagating error information. But if knowing that a particular type of exception is propagating is important to you, you will want to use checked exceptions. Note that this is not a simple "either-or" tradeoff. If you are mapping checked exceptions, you might have only one checked type you map to in an entire subsystem. In terms of effort expended, this would be very close to simply wrapping everything in unchecked exceptions. But you could, instead, provide more fine-grained information and have several checked exception types to map to, each representing different categories of error.

How much you care about the precise nature of exception types will vary depending on the nature of the subsystem you are considering. A simple reporting system may well find checked exceptions overkill, since any exception will typically terminate the production of the report with an error message. On the other hand, a webserver that should be up 24/7 may find it useful to label areas of its code that could experience database and/or socket exceptions, to aid in a robust failover policy.

Conclusions For Java

conclusions

Sun's advice of using checked exceptions where it is likely the client will be able to deal with the problem straight away makes more sense than many people think.

No matter what your personal views on the matter are, you still need to decide how you're going to propagate checked exceptions.

How you propagate checked exceptions should depend on the kind of application you are working on. Neither wrapping in unchecked exceptions, or keeping things as checked exceptions, are bad ideas in themselves. Agree what makes most sense for the area you work in.

beware the zealots

Be cautious of people who argue that Java checked exceptions are always good, or always bad. They probably have such a firm opinion because they have only worked on systems of one extreme or another. Don't uncritically follow the opinion of someone who only has a limited range of experience! If you want to make the right decision for your own systems, look at the errors they will need to handle, and weigh up the pros and cons.