Jun 14 2007
Why do catch clauses need to be ordered?
Looking at question #15 on JDJs Secrets Of The Masters: Core Java Job Interview Questions (Secrets of the masters???!! Whhoaaah!!), I was reminded of the question Vinod once asked me: “Why do catch clauses have to be ordered?”
It is generally known that, in Java, the order of the catch clauses is important. The more specific exceptions have to be handled first followed by the less specific exceptions. So, the following snippet of code causes a compilation error, as FileNotFoundException is more specific than IOException (FileNotFoundException extends IOException).
try{ //Some File I/O operations here }catch(IOException e){ //handle the I/O error }catch(FileNotFoundException fnfe){ //handle the case when the file is not found }
To fix it, you need to change the order in which the exceptions are handled by moving the more specific exception (FileNotFoundException) before the less specific exception (IOException), like so:
try{ //Some File I/O operations here }catch(FileNotFoundException fnfe){ //handle the case when the file is not found }catch(IOException e){ //handle the I/O error }
This change is so straight forward that, any smart IDE can do it for you
So Vinod’s question really was: Why didn’t the designers of the Java language make the compiler smart enough to sort the catch clauses automatically (instead of pushing the burden on to the IDEs/developers)?. To quote him verbatim (including the typos) from my messenger archive: “.. i mean you are asking the programmer to think like compiler than compiler think like a programmer … from a programmer perspective… i want to catch FNFE if the exception is of that type other wise cathc IO”. Interesting point .. I never thought of it before.
Spoiler: I don’t know the answer, what follows are my thoughts or possibly my stream of consciousness, like my article on Why is finalize method protected?.
Having found no clues in The Java language specification I thought the answer probably lies in the history of Java. A Brief History of the Green Project is a good place to start. This page gives history of Java (it was originally called Oak) and has a copy of the version 0.2 of the Oak language specification [PDF]. The spec gives an interesting perspective on how the Java language evolved.
Having found no clues in The Java language specification I thought the answer probably lies in the history of Java. A Brief History of the Green Project is a good place to start. This page gives history of Java (it was originally called Oak) and has a copy of the version 0.2 of the Oak language specification [PDF]. The spec gives an interesting perspective on how the Java language evolved.
Side tracking: Interesting tidbits from the Oak specification
Throwable
was earlier calledGenericException
- Asynchronous Exceptions: one thread can throw an exception (using Thread’s postException() instance method) to another thread
- The protect/unprotect keywords
- You could use
//* javadoc here
notation to write java docs apart from/** javadoc here
print
andprintln
were operators.System.out
was possibly a refactoring- Interfaces declared constants using
const
instead ofpublic static final
. Likeconst int aConstant = 42;
- Supported assertions, preconditions and postconditions
- Has no details on threads, serialization nor does it have a BNF
Side tracking again: Catching multiple exceptions in one catch clause
Many a times people ask me: “Why can’t I catch multiple exceptions in one catch clause, I generally end up pasting same error recovery code in all the catch clauses. Why isn’t a catch clause like a method signature, where I can have a comma separated list of all the exceptions to be handled?” What they want is some thing like this:
try{ //Some File I/O operations here }catch(FileNotFoundException fnfe, IOException e){ //common error handling }
The question itself seems to have the answer. If all the exceptions were listed like method parameters, the above snippet of code would mean “Do the common error handling if BOTH FileNotFoundException and IOException are raised” instead of “Do the common error handling if EITHER FileNotFoundException or IOException is raised”. The solution probably would be to use the OR operator “||” instead of commas? Some thing like:
try{ //Some File I/O operations here }catch(FileNotFoundException||IOException||MyNewException e){ //common error handling }
Incidentally, the Oak specification also compares catch clauses to method definitions. From section 9.4:
A catch clause is like a method definition with exactly one parameter and no return type. When an exception occurs, the runtime system searches the nested try/catch clauses. *snip*
If you have two overloaded methods called handle, of which one takes FileNotFoundException
as a parameter and the other takes IOException
as a parameter, java always knows which method to call. It automatically calls the most specific method based on the runtime type of the object.
private void handle(FileNotFoundException fnfe){ } private void handle(IOException e){ }
Now, as suggested by the spec, each catch clause can be treated as an overloaded method which takes a subclass of Throwable as a method parameter and no return type. Now extending the method overloading analogy shouldn’t java be able to detect which catch clause to invoke? Unfortunately, the complete paragraph from section 9.4 reads:
A catch clause is like a method definition with exactly one parameter and no return type. When an exception occurs, the runtime system searches the nested try/catch clauses. The first one with a parameter type that is the same class or a superclass of the thrown object has its catch clause executed. After the catch clause executes, execution resumes after the try/catch statement. It is not possible for an exception handler to resume execution at the point that the exception occurred.
The question now is, instead of continuing the method definition analogy and supporting overloading semantics to the catch clauses, why does the spec say the first catch clause will be chosen?
One possible reason could be for ease of compiler development. This seems to be an unlikely motivation.
Other possible reason could be for code clarity. What if the java developers start to expect that all the exception handlers that match are invoked? The problem exists with or without auto-sorting of catch clauses. A switch like construct would have been more appropriate then:
try{ //Some File I/O operations here }catch(Throwable t){ switchOnClass(t){ //using a hypothetical keyword switchOnClass case FileNotFoundException: //handle file not found error break; case FileNotFoundException: case MyNewException: //some processing for both FileNotFoundException and MyNewException break; } }
Or the other possible reason is because Java’s exception handling was based on C++’s (as mentioned in the foot notes of the Oak spec page 26). C++ allows multiple inheritance. So my class MusicStreamingException
could extend both MusicPlayerException
and IOException
. Now assume the compiler see’s this piece of code:
try{ if(someCheckHere()) throw new MusicStreamingException(); }catch(IOException e){ }catch(MusicPlayerException e){ }
Both the catch clauses match equally and the compiler has no way of determining which one to invoke. Hence the best policy would be to choose the first catch clause. However, this would never happen in Java as it does not allow multiple inheritance, else the same problem would exist in overloaded methods. Is it possible that this requirement in the spec is only a legacy from C++? And can it be done away with without impacting the existing code?
June 14th, 2007
11:35 am GMT-0700
Comment #1