Every Java developer naturally encounters situations in her or his programming life, where the question arises: Why do I have to deal with this checked exception? Wouldn’t it it much less work, if that exception had been designed as a runtime exception?
String from a
byte array with the default and always supported character encoding UTF-8 is one example:
Dealing with URLs may also be sometimes annoying:
Exceptions are useful. They detach the location, where the problem has been encountered, from the location, where it is handled. But shouldn’t we just use runtime exceptions, since checked exceptions are just annoying?
What makes checked exceptions useful?
Catch what you cannot see
If an API provides a method without documentation and without declaring runtime exceptions, developers can hardly determine, how this interface will react, when leaving the happy path. Even a source code analysis may not be helpful, since runtime exceptions can bubble up from anywhere underneath. You cannot catch what you cannot see.
Even if the runtime exceptions are declared and/or documented, the compiler and IDE may not help you much using the API. If you are using a method that declares a checked exception, your development environment will urge you to deal with it.
Thus, checked exceptions can make API use easier, since they make the intended usage explicit: If the check is useful to be noticed and handled by the caller.
But what are good rules to design exception handling for a library or application?
Faults and Contingency Exceptions
In his article Effective Java Exceptions Barry Ruzek advises to distinguishes between
in an exception design.
Where faults basically are used to signal problems that usually cannot be handled by the client (such as database connection or IO problems), contingency exceptions model business problems (like insufficient funds or overdraft exceptions). Barry Ruzek summarizes
|Is considered to be||A part of the design||A nasty surprise|
|Is expected to happen||Regularly but rarely||Never|
|Who cares about it||The upstream code that invokes the method||The people who need to fix the problem|
|Examples||Alternative return modes||Programming bugs, hardware malfunctions, configuration mistakes, missing files, unavailable servers|
|Best Mapping||A checked exception||An unchecked exception|
Source: Mapping Java Exceptions, Barry Ruzek
Sometimes exceptions are thrown because preconditions are not met. This is caused by passing invalid arguments to the method call. This is usually a kind of programming error and therefore something to be handled as a fault. It may also be a form of validation problem. If this call is initiate by the system, this is a system problem (e.g. inconsistent data) and usually a fault. If the call is initiated by a user input, this is defined as a contingency. The user is informed of the validation problems and may give it another try.
Technical and Business Exceptions
|Technical Exception||Problem that prevents us from using the application.|
|Business Exception||Problem that we get informed of and therefore prevents us of misusing the application.|
This definition matches that of faults and contingencies. A technical exception is a fault, a business exception a contingency. I favor the terms fault and contingency, because I think it is too easy to get distracted by the term technical, where someone might think of
SQLException and therefore associates it with the resource layer. The term business is easily associated with the business layer only. While this is a clear misinterpretation not intended by the author, the term fault is neutral. A fault, a defect that can cause an error, may occur on any layer. A contingency either.
My Contingency is your Fault
Is it a failure to have e.g.
IOExceptions declared as checked exceptions? I think not. From a libraries perspective an attempt to read from a stream that fails, is a contingency the client code has to deal with. Having the compiler check that is really a good idea, since this problem cannot be overlooked.
Is it a good idea to let the
IOException bubble up the call line within an application? I think not. The contingency has to be handled inside the library. If this is not possible, the API designer may want to map the contingency exception to an unchecked or checked exception. Within an application, one would usually see the problem as a fault and therefore propagate it as an unchecked exception. All intermediaries are now free to handle it or not. The application may deal with it in the presentation layer, where the problem is displayed to the client.
Exception chaining in this situation is usually a good thing. It allows you to add context information to the caught exception that is not available at lower layers (Contexts and Dependency Injection may give an alternative solution to this problem). It is somewhat laborious:
But it makes the problem clear for the people who have to analyze it. If
OtherException is a checked exception, adding context information cannot be overlooked. If it is an unchecked exception it may. In an application, where all faults are rooted in a single unchecked
MyFaultException, you may deal with these problems where it best fits, without needing to declare checked exceptions on every method. If there is context you may want to propagate you can always use:
Plenty of declared Exceptions?
In theory there can be plenty of exceptions occur within a component. Each of them has to be documented as part of the API. Having them all rooted in one parent exceptions makes the catching easier, but they still have to be declared individually. As long as dealing with them, checked or unchecked, makes sense from the client’s point of view.
In practice this is not a great deal. If it is a fault, its a runtime exception which may be declared by its parent exception type. If a method is doing just one thing, there is often just a few exception types that are relevant to the client.
The client is usually not interested in the actual fault. Knowing that a fault has been encountered is all that is relevant to the client. Therefore the client deals with the
FaultException only. Declaring and documenting it allows developers to keep possible problems in mind to decide to deal with it or not.
Runtime and checked exceptions have both their advantages, serving different requirements. Faults and contingency exceptions should be considered in designing exception handling. These concepts can be easily applied to smartics Exceptions. You may have a look at Best Practices Designing Exceptions or read the article What is smartics-exceptions all about? for an introduction to designing exceptions with smartics Exceptions.