Checked Exceptions for reactive flow control

In Why I prefer error values over exceptions we looked at the unfortunate characteristics of exceptions, among other things. As we discussed there, unchecked exceptions will automatically traverse up the call stack until they are caught. This makes them extremely useful as a way to reveal programming errors. Therefore it is best not to catch the general unchecked exception (such as Java’s RuntimeException).

Unchecked exceptions should only occur in case of a programming error and on top of that they are not required to be declared. Catching a very generic type such as RuntimeException will most likely result in other exceptions accidentally being silenced. This is bad because, as a result, you will be hiding errors instead of fixing them. Futhermore, the programming error is not obvious anymore, but its side-effects (e.g. corrupted state, writing to a file left unfinished, etc.) may still be there.

On the other hand, it is not recommended to use unchecked exceptions for common alternative paths through your code. Whenever an alternative case can be expected, then you should not use unchecked exceptions. Common examples are events such as: losing a network connection, errors that happen while performing any kind of I/O, upon establishing a network connection finding out that the host is unreachable, while verifying data finding out that data is bad. These alternative cases should be part of your code. They are clearly not programming errors. Java’s checked exceptions provide a mechanism to do that where Java’s syntax provides you with hints on which code paths must be handled according to the known (declared) exceptions that may occur.

Kind of exception determined by context

The actual type of the exception depends not only on the type of error but very much on the context and the semantics of the method. Java’s IllegalStateException is an unchecked exception that is thrown when you call a method at the wrong time, i.e. the object is in the wrong state and the method is not applicable right now. This is a programming error, as you forgot to call some other method first which puts the object in the correct state.

However, depending on your context, illegal state might be a common event. Take, for example, an implementation of a protocol which must process requests from the local user as well as the other party on the other end of the network connection. Processing happens concurrently as these are both independent actors. Effectively, it is very reasonable that processing happens in parallel. As a consequence there is a known race condition: messages from local user compete with messages from the network inside the protocol that processes the messages. The message that is received first, is processed first. This is independent of whether it originates from the local user or the party on the other end of the network connection. Now we have a common event that may only be discoverable at the earliest when the message is processed. The checked exception is there to define the alternative path that is (reactively) being taken when such an event occurs.

A similar case can be made for data verification. A method might assume that it receives a positive integer value. When a negative value is provided, a calculation fails and as a result an unchecked exception is thrown. The programming error is that we let the negative value slip through into that method. However, we can create a verification method which sole purpose is to do verification of the value. Now we would expect a checked exception to be thrown inside the verification method in case the data is bad. For a method that verifies data, this is a reasonable alternative outcome - an expected outcome - that must be handled accordingly.

Reactive flow control

Checked exceptions, because they are supported by the language, are a nice way to do reactive flow control. That is, you divert onto an alternative path of execution when such an exception occurs. Unchecked exceptions are inconvenient as you can easily forget to handle a possible alternative. Not to mention the case where a method gets modified and new alternative cases arise.

Checked exceptions are one step closer to your every-day error handling of expected alternative cases. You are forced, by the language, to handle defined alternatives. Concurrent cases can be handled reasonably well, without having to litter the code with if-statements all-over. Some of the other disadvantages of exceptions still hold, but checked exceptions most definitely provide advantages over unchecked exceptions.

Usage guidelines

Some general guidelines on the use of checked exceptions.

This post is part of the Error Handling series.
Other posts in this series: