re: Java’s checked exceptions were a mistake

# 2003-04-03 15:03:42 -0500 | Java | 2 Comments

On Java’s checked exceptions were a mistake (and here’s what I would like to do about it), Rolf Waldhoff introduced a “tunneling Exception through RuntimeException” concept:

I’ve grown increasingly fond of this approach, and think I’ll try to put something together in the Jakarta Commons sandbox for it.

I find checked exceptions painful for exactly the same reasons as Rod, but I don’t personally think they were a mistake. I like the “defensive programming” style, and I don’t mind being forced to be explicit about exceptions. The problem is with “middleware”, of course: how does a lower-level communicate checked exceptions to the higher-level without the “middleware” getting in the way.

Generic Java (JSR14) does introduce a way to mitigate this problem. It’s not a 100% solution, but it has it’s place. Generics allows you to use an Exception as a type parameter; so you might re-define the UnaryPredicate in Rod’s example like this:

interface UnaryPredicate&ltT, E extends Exception> {
    boolean test(T obj) throws E;
}

This says that UnaryPredicate has a method test() that takes an argument of (some) type T and may throw an exception of (some) type T. The select() method then gets implemented this way:

public static <T, E extends Exception>
Collection<T> select(Iterator<T> iter,
                     UnaryPredicate<T,E> pred,
                     Collection<T> col)
    throws E
{
    while(iter.hasNext()) {
        T obj = iter.next();
        if(pred.test(obj)) {
            col.add(obj);
        }
    }
    return col;
}

Is that scary enough? Generics is cool, but make no mistake — Generics is complex. You would then use this method as follows:

class IsDirectory
   implements UnaryPredicate<String, URISyntaxException>
{
    public boolean test(String obj) throws URISyntaxException {
        URI uri = new URI(obj);
        File file = new File(uri);
        return file.isDirectory();
    }
}

...

Collection<String> allFiles = getListOfFiles();
Collection<String> directoriesOnly = new ArrayList<String>();

try {
    CollectionAlgorithms.select(allFiles.iterator(),
                                new IsDirectory(),
                                directoriesOnly);
} catch (URISyntaxException e) {
    // thrown when someObject isn't a valid URI
}

I think that’s a neat bit of syntax in use, but not in declaration. That is, it’s nice to use select(), but the implementation of select() is a bit of a mouthful.

2 Responses to this entry:

  1. Oliver Burn Says:

    That is an interesting approach using Generics. Just looking at the example convinces me that Generics will be taking the complexity of Java to a new level. This is good for people with big brains, and bad for the rest of us. It reminds me of my days in C++ using the templates.

    I guess Generics are like razor blades. A very efficient tool in the hands of an expert, but very dangerous in the hands of somebody who does not know what they are doing. Personally I am not desperately waiting for them.

    Out of curiosity, what if do not want my test() method to throw an exception. With the example it looks like I am forced to throw an exception. I guess I could use the RuntimeException, except your declaration says the exception must extend Exception?

  2. Malcolm Edgar Says:

    I am moving towards using unchecked exceptions after years of using checked exceptions for everything, except illegal arguments. The code is easier to read and write, and in most frameworks there is a top level exception handler which will do something sensible.

    I think when you have resources which must be cleaned up, such as Connections, I/O Streams and Files, you should use checked exceptions. Otherwise I think unchecked exceptions are fine, so long as you javadoc these in your methods.

    I find nesting causing the exceptions in very handy when designing exception handling code.

Leave a Reply

Click here to leave a reply