Interface Evolution and Exceptions in Java

I’m firmly in the camp that favours the checked exception style used in Java as opposed to the unchecked approach used by C#. For me it’s as fundamental as explicit declaration of variables. Java is, in general, a statically typed language and, for me, checked exceptions are part of that style. It’s a different story in more dynamic languages such as Python. I do recognize, however, the fact that some people have strong opinions to the contrary and I’m not out to convert anyone here.

I have come across an issue where the explicit declaration of exceptions does cause a problem. Today, when you declare in Java that your code throws a particular exception, you can never take that away that without breaking somebody’s code. You can’t evolve the interface in a graceful way. A recent case in point involved a seemingly innocuous change to BCEL. The signature of a constructor changed from

public ClassParser(String file_name) throws IOException

to

public ClassParser(String file_name)

Unfortunately that broke the following code in Ant, since the compiler says, quite rightly, that the code does not throw an IOException.


try {
new ClassParser("force");
} catch (IOException e) {
// ignore
}

Here is the crux of the problem. If we changed Ant’s code to work with the new BCEL code, then Ant would not be compilable against the old BCEL. Ant’s and BCEL’s releases would need to be synchronized which is not a good thing.

I added the exception specification back in to the BCEL code but it’s not a very satisfying approach. The BCEL constructor doesn’t throw the exception anymore and there should be a way of saying so, of evolviong the interface gracefully. The best idea I could come up with was adding something like a @deprecated_throws indication which indicates that the method used to, but no longer, throws a particular exception. The compiler would emit a deprecation warning on any code that caught this exception. As with other deprecated usages in Java, it would not cause an error. Such a mechanism would allow Ant to compile with both the old and the new versions of the BCEL code. The Ant and BCEL releases would be decoupled and sufficient time would be available for the BCEL change to propagate through its users.

2 Replies to “Interface Evolution and Exceptions in Java”

  1. And the same problem faces when you try to add a new checked exception to an interface. So you have to think twice (but that’s what evolution is about – you can’t figure out everything in advance 🙂 ). Still cheked exceptions are good because they make you think about handling them. But most of the times you need unchecked ones (which of course can be done in java).

  2. This is probably not an issue anymore, but I’ll add my 2 cents nevertheless.
    How about a bad hack to guard against this particular api change?

    Something like:
    try {
    new ClassParser(“force”);
    if (false) {
    // will never execute but ensures compile time compatibility with older
    // BCEL versions
    throw new IOException();
    }
    } catch (IOException e) {
    // ignore
    }

Comments are closed.