Generic exceptions

This post is about a language mechanism they snuck into Java in 5.0 which I recently discovered. Well, to be honest I didn’t even discover it myself: most of what I’ll describe here was pointed out to me by Peter Ahé. But as far as I can tell it’s not widely known or used so I thought I’d take the opportunity to spread the word.

Here is a standard generic method, just to refresh yor memory and get into gear:

public static  T getFirst(List ts) {
return ts.get(0);
}

It takes a list and returns the first argument. It has one type parameter which is used both in the return type and in the argument types.

It turns out, however, that you can actually use type parameters in one more position in a method declaration: throws clauses:

public interface IRunnableextends Throwable> {
public void run() throws T;
}

This interface has a single type parameter which specifies which kind of exceptions may be thrown from the run method. I’ve always just assumed that you couldn’t do that so I’ve never even tried — but it turns out that you can. As far as I can tell there is no explicit rules for this in the language specification; it’s just not disallowed. The only mention I’ve been able to find of it in the JLS is a footnote in section 8.4.6.

A concrete subclass of IRunnable could be

public class FileCreator implements IRunnable  public void run() throws IOException {
// create a file
}
}

If you call the run method on a file creator you will be required to catch or declare the IOException, just as usual. What if my runnable doesn’t actually throw any exceptions? Well, you can just use IRunnable.

Since the exception type is just an ordinary type parameter, you can do all the complex stuff you can usually do with type parameters. You can leave them out:

IRunnable rawRunnable = ...;
rawRunnable.run(); // throws Throwable

and you can use wildcards

IRunnableextends DebugException> debugRunnable = ...;
debugRunnable.run(); // throws DebugExceptions

There is one thing you can’t do, though: throw multiple exceptions:

public class MyRunnable implements IRunnable<...&gt {
public void run() throws IOException, IllegalArgumentException {
// ...
}
}

As far as I can tell, there is no type argument you can give to IRunnable that would allow run to throw more than one exception type.

In my checked exceptions RFE, one argument I used was that if you write a general visitor there is no orderly way to propagate checked exceptions out of the visitor traversal — you have to handle those exceptions locally. But that is not actually true: you can just parametrize the visitor with the kind of exception it might throw and then parametrize it with a checked exception if any of the methods might throw one. I’m not sure I would do it that way, but it’s certainly a possibility at least in the cases where the visitor can only throw one kind of exceptions. But while this mechanism solves that problem, it creates another.

Since this mechanism relies on generics it can also be fooled, as you can with traditional generic classes. Here’s an example of fooling the static type system:

ListList ints = (List) strs; // warning, but legal
Integer i = ints.get(0); // cast error

With generics some operations, such a the conversion from a list of strings to a list of integers, cannot be checked at runtime. Instead, the runtime system has to wait until it has enough information to perform the check. In this case, instead of checking that the list is really a list of integers in line two, it checks that the element extracted in line three is an integer.

The reason why this works is that java’s generics were designed such that even though you might fool it in some cases, you will always be caught before you can do any damage. The type system may not be able to tell a list of strings from a list of integers, but if you try to assign something to an integer-valued variable that the static type system is unsure is an integer, it will insert a runtime check.

For this to work, however, you must eventually be in a situation where you have all the type information available to insert the check. And with exceptions that is no longer the case. Observe:

extends Throwable> void cloakException(Throwable e) throws T {
throw (T) e;
}

void myMethod() {
try {
throw new IOException("Harsh!");
} catch (IOException ioe) {
this.cloakException(ioe);
}
}

Here cloakException performs an unchecked cast to T and under normal circumstances the caller of cloakException, who has all the type information, would have been able to insert a delayed check that the conversion was really legal. In this case, however, the object is not returned but thrown. That means that the caller never gets a chance to verify the conversion. This essentially provides a way to circumvent the checked exception mechanism and throw checked exceptions freely. If you run the above code, myMethod really throws an IOException without declaring it. How about that.

It’s not something I would recommend as a programming technique, though. The thing is: you can’t really catch them again. If you want to catch a checked exception from a piece of code, the compiler will give an error if it can’t prove that the code might throw that exception. And since the whole point of the hack above is that you can throw checked exceptions without declaring them, by definition, the compiler won’t allow you to catch them again. So it’s really more an odd curiosity than a useful programming technique. But then maybe that is the case for the whole mechanism of throwing type parameters.

2 Responses to Generic exceptions

Leave a Reply to finnw Cancel reply

Your email address will not be published. Required fields are marked *


*