Standard ExceptionsSource: firstname.lastname@example.org
Problem: What's wrong with specifying the exact type of exception a method may throw? And how do some people get away with using only a small number of exception classes for every kind of exceptions?
Phil Goodwin wrote:
Checked exceptions in Java create a dependency between a throwing method and all of its callers. That's a very steep price to pay for a service and, as it turns out, the service provided by checked exceptions isn't terribly valuable. What you get is documentation about the types of exceptions that can propagate. That may seem like a good thing but in practice all the non-handling code needs to know is that some kind of exception can propagate. The exception handling code usually doesn't need the type information either but if it does it can be retrieved by having multiple catch clauses. Non exception handling clients benefit from knowing when a method they are calling can throw so that they can clean themselves up before allowing the exception to propagate. So the type information in the "throws" clause of a method declaration only serves two purposes: as documentation about what sort of exception handlers to write in the rare case that types of exception need to be distinguished and as a notification that an exception can propagate out of the method.
In my experience exception handlers are rare. They appear either very close to the UI or very close to some sort of system boundary that requires error information to be marshaled. Robert Martin writes elsewhere that long distances between exception throwers and exception handlers is undesirable. My experience has been just the opposite: the longer the distance between the thrower and the handler the greater the chance that exception handling is the correct error handling strategy. My experience is also that all of the handlers implement the same error handling strategy -- usually by calling some function that does the actually handling. What this indicates is that the best strategy for writing exception handlers is to devise a strategy for handling each type of exception (preferably with most types handled in a single, uniform, way) and then implement that strategy in every handler throughout the system without regard to the types of exception that a particular handler can be expected to be exposed to. The advantage of this strategy is that it provides a single, coherent, strategy for exception handling. Both the exception handlers and the exception throwers are coupled to the pool of available exception types (a fairly stable abstraction) but not to each other. The disadvantage is some duplicated code in all of the handlers. In practice this code is easy to maintain because it is fairly stable and easy to find.
Methods that contain exception handlers aren't the only ones that need to be aware of propagating exceptions. A propagating exception is equivalent to aborting a database transaction. All the work done starting from the initial call in the "try" clause up to the point of the exception throw should be rolled back to its initial state, or in the worst case rolled forward to some stable state. In order to write code that is exception safe an author needs to know which methods might throw exceptions, so that the rollback code can be put into a finally clause, and, which methods cannot throw exceptions and are therefore safe to call after rollback becomes impossible. The latter sort of methods are critically important to writing correct programs. It would be a grave error to create a method that doesn't throw exceptions, use it in a situation where exceptions aren't tolerated, and then change it so that it does throw an exception.
Based on this analysis this is the strategy that I use and recommend for exception handling in Java: declare every method with "throws Exception" as the default. Methods that MUST NOT throw exceptions are declared without a "throws" clause (if it were my language to write you'd have to specify something special for a method that DOESN'T throw rather than one that does). If I need something more specific than "throws Exception" then I put it in -- it hasn't happened yet so I can't be more specific. If it happens that a method doesn't actually propagate any exceptions and I need to use it in a situation where it won't be allowed to then I remove the "throws Exception" clause. Having to rewrite a non "throws Exception" method so that it does throw is a big deal because all of its clients have to be checked and possibly rewritten.
My exception handlers almost always either translate the exception into some other form of error for further propagation or they ask the end user to make a decision about how to proceed. I anticipate that, on occasion, I'll write exception handlers that try an alternate strategy to accomplish the original task, but I have not yet had the opportunity to do so.
Bill Venners, Exceptions in Java