Checked Exceptions - A Failed Experiment

When swapping between Python and Java regularly one of the things that strikes me is what a PITA checked exceptions are. 

I can see why they might have seemed like a good idea at the time but over the course of 20 years or so they have been proven to be nothing but an inconvenience at best. If they were a success then we would have seen other languages such as C# introduce them. 

The rationale behind checked exceptions is that calling code be forced to explicitly deal with an exception or deliberately raise it further up the call stack. 

An Example

Recently I have been dealing with various file level operations in Java and am constantly forced to deal with a variety of checked exceptions. Something as trivial as reading a properties file is made harder and more tedious by checked exceptions. 

Assuming we have a reference to a File object then we want to wrap this in a FileReader object using the following constructor:

public FileReader(String fileName) throws FileNotFoundException

The first thing we have to deal with is a FileNotFoundException

At this point we ask ourselves what do we want to do here? The properties file contains configuration for the application we are starting. We can't proceed any further without it and it's absence indicates it has either not been installed correctly or we have supplied an invalid path. 

Three terrible solutions I often see employed at this point are:

  1. Return null from the method and let the calling code check to see if a properties file has been returned. 
  2. Declare our method to also throw a  FileNotFoundException  and raise it. 
  3. Swallow the exception and carry on. 

The first option isn't appealing, we shouldn't force calling code to be defensive and check for null any time we return something.

The second option leaks implementation details further up the call stack and can make refactoring painful. 

The third option is a real WTF but I have frequently seen it done. This will generally always lead to a failure sooner or later, the longer it takes to manifest the harder it will be to track down. 

Let's put off working out what we want to do with that one and look at the next method call we need to make, which is calling load on a Properties object and passing it our FileReader. Here is the method signature:

public void load(Reader reader) throws IOException

Great, another checked exception to deal with. The name of the exception sounds quite vague though, let's look at the javadoc:

Throws:
    IOException - if an error occurred when reading from the input stream.
    IllegalArgumentException - if a malformed Unicode escape appears in the input.

I am not really any the wiser after reading that; the checked exception could cover a multitude of sins when reading the file. We did learn that there is a specific condition with invalid Unicode escapes that we aren't forced to handle as it is thrown as a runtime exception. That seems quite inconsistent doesn't it?

The solution

As I explained earlier the rationale is that calling code be forced to deal with error conditions they might recover from. I believe that exceptions are for exceptional circumstances and as such mainly can't be recovered from. 

In the case of a file if there is a possibility that it might not be there then your code should be able to cope with that fact. 

In our properties file example we were loading application configuration from the file system. If this wasn't present then there is no recovery, the program needs that information or it can't function correctly. 

The approach I take is to create helper methods that catch the checked exception and convert it into a suitable runtime exception. Generally I fall back on IllegalStateException that is available as part of the JDK rather than creating my own and provide a suitable message when constructing them. 

So the code to load a properties file might look like:

public Properties loadApplicationProperties(File file) {
    FileReader fileReader = fileReader(file);
    return loadProperties(fileReader);
}

private FileReader fileReader(File file) {
    try {
        return new FileReader(file);
    }
    catch (FileNotFoundException e) {
        throw new IllegalStateException("Unable to create FileReader", e);
    }
}

private Properties loadProperties FileReader fileReader) {
    Properties properties = new Properties;
    try {
        properties.load(fileReader);
    }
    catch (IOException e) {
        throw new IllegalStateException("Unable to load properties", e);
    }
    return properties;
}

This also has the advantage of pushing a lot of the noise down into the helper methods leaving the top level application flow clear in the orchestrating method. 

This type of programming model should be familiar to anyone who has used the Spring Framework as it is a common pattern they use.