Recap: Exception Hierarchy In Java
In Java, we have three types of Throwables :
- Error and it’s subclasses
- Runtime Exception and it’s subclasses
- And the rest (Non-Runtime Exceptions)
Exceptions those lie in the third category are called Checked Exceptions. If a method throws a checked exception, the compiler makes sure that the API user handles it either by using try-catch block or by propagating it to the higher layer by adding a throws clause to the method definition. The throwables belonging to the first and second category are unchecked in nature. Compiler does not force the API user to handle them.
The Thumb Rule
As an API author (Somewhere I read that every method is an API, if it is a public method then it is exposed to the world, if it is a private or package-private method it is an API for internal usage. I liked the concept), you should use a checked exception only when you believe that the API user can recover from the exceptional situation. If it is not recoverable, Runtime Exception (unchecked throwable) should be used. As a rule, errors are generally reserved for JVM related issues (e.g OutOfMemoryError, StackOverflowError etc). So, if a custom exception needs to be written which you believe, should always be recoverable then extend it from the Exception. Otherwise for any custom unchecked exception derive it from Runtime Exception. Hence,
- Expected + Recoverable -> Checked Exception
- Unexpected + Not Recoverable -> Runtime Exception
Problems with Checked Exception
1. As mentioned earlier that a method should throw Checked Exception, if the API author believes that the API user can recover from the exceptional situation. But, in real life, whether an exception is recoverable or not depends on the situation – for the same piece of code one exception may be recoverable in one context and may not be recoverable in some other context. Let’s take up one example. I work with telecom subscriber provisioning system. There lies a method which validates the cell phone numbers of subscribers.
- If an operator tries to create a new subscriber through the UI by providing an invalid number, the Number Validation Method should throw back a checked exception, because it is recoverable. The operator would be asked to re-enter a valid phone number.
- The operator is trying to search for the number of a particular subscriber. It is being read from the DB and then validated using the same Number Validation Method. If the number is found to be invalid, the method should throw an unchecked exception, because it is NOT recoverable this time. The subscriber data has got corrupted in the DB.
By throwing a checked exception, the API author is forcing the API user to handle it. If there is no way to “actually” handle the exception (i.e. recover from the exceptional situation), what will the user do then? In such a situation, most of us do one of the following:
- Swallow the exception by providing an empty catch block (This is nothing but a bug)
- Provide a catch block just to log the occurrence of the exception. (This is also the same)
- Catch the exception and re-throw it.
This is true in 90% of the cases. Of course, there is a so called better solution – catch the exception, convert it to a more meaningful checked exception and pass the ball to higher level. But, this may lead to another problem as indicated in point number 3 below. The ideal solution, I believe, is to catch the checked exception and convert it to an unchecked exception. Although, in Java, the concept of try-catch was introduced to handle the exceptional conditions, but it’s very rare to find a catch block where the exception is really handled.
The bottom line is:
- Things may go beyond API authors “belief” and quickly become messy.
- Most of the time we use Java’s (checked) exception handling machinery just to log the exception (and then raise a bug and fix it).
2. In my application, I wrote an API which throws one checked exception E1 and published the API to the world. Now, because of some new features, the implementation of the method has to be changed and it has to throw two new exceptions, say E2 and E3. If E2 and E3 are checked exceptions, all the client code which are using the API till date, will break. The way outs are:
- Catch E2 and E3, then convert them to E1. I believe that defeats the use of exception.
- Publish new version of the API.
3. try, catch is not the only way to handle a checked exception. If the exception can not be immediately handled, it may be propagated to the higher layer by declaring throws clause. In the higher layer of the stack, the exception can be handled in a better way. This may also be problemetic in cases. Say, for a large, layered application one new API is being developed. It internally invokes three other APIs. Each of those three methods in turn throws three checked exceptions. If the new API is not in a position to handle any of those exceptions immediately (inside the method code), how many exceptions would be thrown back? NINE. Will not be it difficult for the client to use the API?
4. Another use case of checked exception is worth mentioning. Say, for an UI application, all the methods in all the classes throw the same user defined checked exception called GuiBaseException. Different instances of same exception bubble up from different layers of the code in different exceptional situations and all of them gets handled by the same exception handler at the top most layer. With this approach, everybody is happy – the API author is happy because he believes that “recoverable” exceptions should always be checked, the API user is happy because he is handling them without any pain and effort (also without making the code dirty by introducing try and catch here and there). Wonderful solution, but it completely defeats the very idea of exception handling!
Though Checked Exception conveys an warning message to the user “Heey! you need to pay attention”, but in most of the cases, that warning is ignored. With the usage of ONLY Unchecked Exception (i.e. by avoiding checked exceptions), problems 1, 2, 3 can really be addressed. No body is going to force the API user to handle the exception. Every thing would be properly documented. The API user would decide whether to handle it or not depending on the context.
Ok, this one liner may not be a full proof solution. In fact, I am not trying to give a solution also. The aim of this post is just to capture the problems with checked exceptions. Yes, accept it or not; Checked Exceptions are problematic. Java did an experiment by introducing this concept with a lot of great expectations, but as per my understanding, over time it has been proved to be a failure. C# does not have checked exceptions and the same is true for other JVM based languages like Groovy and Scala.
Dear Reader, what do you feel about it? Which camp are you in – Checked or Unchecked? 🙂