re: Java’s checked exceptions were a mistake
# 2003-04-03 15:03:42 -0500 | Java | 2 CommentsOn 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<T, 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.
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?
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.