Checked Exceptions Reality Check
Mark Hooijkaas, $Revision: 1.3 $, $Date: 2004/05/31 23:34:36 $
The beauty of exceptions
In the C family of programming languages (C, C++, Java, C#), exceptions were
first introduced with C++. In C, and other programming languages
without exceptions, dealing with errors or other “exceptional
circumstances” was very cumbersome and error-prone:
-
Functions use different kind of mechanisms to signal that some kind of problem has occurred,
such as special return values or global error variables.
-
Each function call must be surrounded by code to check for any error.
Sometimes it is estimated that 90% of code is about dealing with exceptional circumstances.
-
There is a very real possibility that programmers forget to check for an error every now
and then (or consistently), thus ignoring the error.
Without exceptions writing a simple statement like
config_file = fopen(filename, “r”);
becomes something like
config_file = fopen(filename, “r”);
if (config_file==NULL) {
/* clean up */
set_error(PROBLEM_READING_CONFIG_FILE);
return;
}
The use of
exceptions solves all of these problems:
- uniform mechanism to signal errors
- no code clutter from continuously checking for errors
- no possibility to accidentally ignore errors
Checked and unchecked exceptions
In the Java programming language something new was added to the exception
mechanism: the checked exception. A checked exception is any
exception that inherits from Exception but not from RuntimeException.
If there is a possibility that a java method can throw a certain kind
of checked exception a programmer must explicitly mention this in the
interface of this method (the “throws clause”). The
compiler checks if a programmer has done this correctly, and reports
an error if in the implementation of a method a certain type of
exception can occur that is not explicitly handled by the programmer
by either catching this error (or any parent class) or mentioning it
in the throws clause (or any parent class).
A slightly different view on checked exceptions is that by mentioning a specific
exception type in the throws clause of a method, forces any user of
that method to handle that error by either catching it, or by
mentioning it in it’s own throws clause.
Checked exceptions in practice
On first sight, I (and any other programmer I know) really liked this idea of
the compiler helping a programmer in ensuring that all possible
errors has been handled or acknowledged. Forcing a programmer to
handling or acknowledging each possible type of error, should help
make code more robust.
The idea of checked exceptions still seemed to be a great idea on second sight
when compiling small and medium sized programs.
It often is a pain to get all the throws clauses right.
Also, when some implementation deep down changes one often has to
change the throw clause of many methods to propagate a new exception
up call trees.
However the idea is that all this work to get a program to compile correctly would
benefit the robustness of the program.
However, when one looks at the practice of large programs, with often
many programmers working on the same program, certain problems in the
use of checked exceptions appear:
-
In large programs it becomes unworkable to list all possible exceptions
in the throws clause of a method.
-
The throws clause of a method may reveal details of some implementation
detail deep down.
-
When changing the implementation or another detail of some function that
changes the exceptions that might occur, this might impact all methods
that use this function directly or indirectly.
This might result in a massive amount of methods (sometimes over 100)
of which the throws clause needs to be changed.
Especially the last issue is troublesome when using agile methods, where
code should be designed to be flexible so it easily can be changed due to
changing demands or otherwise refactored.
Exception Wrapping
Most projects have adopted some kind of Exception Wrapping mechanism.
Basically, in a method, one catches a certain type of exception, and
throw a different kind of exception, while trying to preserve as much
information as possible.
In recent Java versions it is possible to include the original exception
when creating a new exception, thus the term exception wrapping.
The following code fragment is an example of exception wrapping
try {
readConfigFile(filename);
}
catch (IOException excep) {
trhow new ConfigFileException(excep);
}
Exception wrapping is indepedent of the issue of Checked Exceptions.
Even for unchecked exceptions (e.g. as used in C++, C# or Python),
it can be useful to transform low-level exceptions into
higher level more specific exception types.
In Java one must use some kind of exception wrapping to mitigate the problems of
checked exceptions as mentioned in the previous section:
-
By wrapping many different kind of exceptions into a new "higher-level"
exception type, the throws clause can often be shortened.
-
By wrapping exceptions caused by implementation details, these
exceptions are no longer visible in the throws clause.
-
When the throws clause of a method changes, the callers may wrap this extra
exception, so that this changes does not need to be propagated over the entire
call trees.
Without exception wrapping, it becomes impossible to realize large projects.
Unfortunately, often the sole purpose of the exception wrapping seems to
be to satisfy the compiler complaining about checked exceptions.
In these cases there often is one or a small number of base exceptions that
alle detailed exception classes derive from.
For example, in a project there might be a ProductXyzException class, or sometimes
each there are PackageAbcExceptions and PackageKlmExceptions classes for each
major package.
All methods can then just claim to throw such an exception (and al it's derived
exception classes), and new exception classes can then be added without
a need to change the signature.
It is needless to say that this is not a good practice, because all information
about what kind of exceptions may be thrown is lost.
Any method that has a "throws ProductXyzException" basically claims "I can
throw many different kind of exceptions, I really dont have any idea
Why I still don't like checked exceptions
Some people believe that using a well designed Exception hierarchy,
can solve the problems with checked exceptions.
I agree that in theory a designer should carefully design an API with
logical / functional exception specifications.
However, I would like that exception hierarchy to use unchecked exceptions,
since a well designed exception hierarchy is not only possible with
checked exceptions.
The fundamental problem with checked exceptions is that
-
A checked exception forces the calling method to conciously handle
that exception, by either catching it, or acknowledging it in the throws
clause.
-
In 99% of the methods a method cannot sensibly handle an exception
other than passing it through to it's caller (either wrapped or not).
However, in reality