I'm finding myself busy refactoring my pet project and removing all the noise associated with exception throwing declarations. Basically, everything that violates a condition is assertion violation or, formally, AssertionError, which in Java is generously allowed to be omitted from method's signature. My question is: what is the point of having Exceptions hierarchy? My experience is that each and every exception is unique, and there is no formal criteria to establish that one set of exceptions is a subset of the other. Even distinction between checked and unchecked exceptions is fuzzy, why, for example, would I insist on the client code catching exception when a lazy (or impatient) programmer can easily wrap it into RuntimeException and rethrow it on me?
In theory the Java exception hierarchy makes a certain amount of sense:
Throwable*
-> Error (OutOfMemoryError, etc.)
-> Exception (IOException, SQLException, etc.)
-> RuntimeException (IndexOutOfBoundsException, NullPointer, etc.)
Now the theory behind these actually makes a certain amount of sense. (The actual implementation leaves something to be desired because of accumulated cruft, sadly.)
Error
-descended Throwable
objects are serious errors that a program is not expected to be able to recover from. (You generally don't catch these, in other words.) One of these popping up is a serious overall system problem. For example when you run out of memory, this represents a serious failing somewhere since in theory the GC has by now desperately tried to free space for you. Catching this is pointless.
Exception
-descended Throwable
objects are all errors that the program can reasonably be expected to encounter during normal operations; things like network errors, file system errors, etc. Indeed with the exception of those descended from RuntimeException
, it is mandated that programs explicitly handle these errors – they're the so-called "checked exceptions". (Of course bad programmers will "handle" these by stubbing them out, but that's a programmer problem, not a system problem.)
RuntimeException
-descended Throwable
objects are slightly different. They are errors which should not necessarily be expected but which a program could reasonably recover from when they occur. As such they are not checked (programs are not obligated to handle these) but they may if there are reasonable ways to handle the situation. In general these exceptions represent programming errors of some sort (as opposed to the previous category which are expected errors which occur in normal operation).
So does this hierarchy make sense? Well, at some level it seems to. Error
is thrown by the system and represents a major failure that's probably going to kill your program. RuntimeException
, when used properly, is thrown by the system libraries (or occasionally by your own program) and generally means someone screwed up somewhere, but it's OK because you might be able to recover from it. Other Exception
objects are expected errors that are actually part of the stated interface of your objects.
But...
That last item is the problem. Checked exceptions are, to put it bluntly, a serious pain in the lower reaches of the torso's anatomy. They needlessly clutter up the code with exception handling boilerplate in such a way as to render, in my opinion (and many others' I might add!), the whole point of exceptions moot: separation of error detection and error handling. By forcing every method in the chain to handle the exception—even if it's just to rewrap it and pass it on!—the code gets cluttered with minutiae of error handling to the point that it is little better than returning status codes and handling them after each method call.
Were Java a smarter programming language the checked exceptions would be checked at compile/link time to see if they were properly handled system-wide, not at each and every method call in each and every class file. Unfortunately Java's entire architecture doesn't permit this level of whole-program analysis and the result is, again in my opinion (but again shared by many), actually a blend of the worst of the two worlds of exception handling and error returns: you get most of the boilerplate scaffolding of explicit error returns but you also get the COME FROM
-like behaviour of exceptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With