Jotting #9: To be checked or Not to be checked – That’s the Java Exception

2007-11-13

Always arousing some emotions (see this recent example), this question whether you should use checked or unchecked exceptions in Java. Other languages don’t have that “choice”, so this is a pure Java problem. Elliot Rusty Harold posted his rules about this problem, introducing so-called external and internal exceptions, some time ago in July. I think these are too complicated and unnecessary but I promised myself to write about it.

Let me come out with my point-of-view right now: Avoid checked exceptions, stay away from them. They are not worth it. OK so that’s out. But some clarifications are in order:

  • I do not argue against declaring exceptions in a method’s signature:
    • In Java you can list both, checked and unchecked, exceptions in the method signature.
    • You should document the most important contract violations that the method will reject.
    • Better still, document the contract in the spirit of B Meyer’s Object-oriented Software Design.
  • I don’t ask that rather obvious requirements, like arguments not being null, are documented in detail.
  • I argue against the rule that checked exceptions have to be declared in each method of the calling stack unless they catch the exception.

What is the main purpose of an exception? It’s to indicate a contract violation (I think Bertrand Meyer’s language is the most appropriate to use).

Does the method raising the exception care about the context in which it is called? No, the method provides its own context: its contract. The rules of ERH about internal and external exceptions make no sense whatsoever in this model. Whether I can write to a file (object) or not, whether the provided argument is of the wrong type or not, whether the object’s state is within the method’s control or not, is irrelevant and is not the question that is being asked.

The method asks a much simpler and more general question: Do you obey my pre-conditions? It does not care what the intent of the caller is, what the calling context or the semantics are. Only the programmer may know that. No, the method just promises to fulfil its side of the contract (post-conditions) if the pre-conditions are fulfilled.

If a file instance is passed as an argument, the file is always outside the control of the called method. If it cannot write to the file because the file doesn’t exist or is write-protected, the method doesn’t care. It just says: You’re not fulfilling the pre-conditions of my contract; here is my exceptional response.

The whole distinction between external and internal exceptions makes no longer any sense (and you could always argue ERH’s examples in different ways depending on context). So we established that any distinction between checked and unchecked exceptions is based on some arbitrary classification of the caller that cannot be maintained inside the method throwing the exception. (You can easily do this for any other model, the checked-unchecked cases are always based on such classifications.)

So we are left with the question whether the compiler-enforced rule on checked exception provides any benefits, and we have to ask the questions:

  • Is it beneficial that every calling method repeats the exception in its signature?
  • In a proper object-oriented design, who carries the information about the problem?
  • Where is the exception being handled and resolved?

The first question should remind you immediately of DRY: don’t repeat yourself. The answer to the second is obviously the exception itself, not the method signature. And the final one is more based on the experience that, at least in applications with a user-interface, it is standard practice to throw the exception back to the user rather than to resolve the problem programmatically. There are some exceptions (no pun intended) like in message-based applications where the queue may try to re-send the message a few times and finally succeed, adding some robustness, but ultimately the problem is thrown back to the user (or left on the dead-letter queue!).

Developers of embedded systems could tell you how hard it can be to resolve exceptions adequately to prevent the system from crashing. Sometimes they just reset the system and log the fault; sometimes they just skip the faulty data and continue (and interpolate, for example, when taking sensor readings). (You may want to read more on this issue and exception design & patterns in the recent series of IEEE Software articles by R Wirfs-Brock.)

In the end we are left with the conclusion that Java’s checked exception were an experiment of good intentions but one that ultimately failed and didn’t provide any significant benefits but rather gave rise to a new anti-pattern: the silently caught exception a la catch(Exception e){ /* do nothing */ }.


Jotting #4: Revealing intent …

2007-07-29

Java does not support Design-by-Contract (pre- and post-conditions, invariants) and asserts are really just a weak placebo.

Nevertheless, you can at least indicate your intentions to some degree with labels:

public void foo(Bar theX, ...) {
   pre_condition: { vetoArgument( null==theX, "Bar argument is null" ); }
   // some code
   post_condition: { ... }
}

where vetoArgument is some helper method to throw an IllegalArgumentException or the like.

Labels are a somewhat under-used feature of Java. But it’s still not the full DBC feature I’d like to see. Annotations will not be able to substitute for proper built-in language support. How long do we still have to wait?


Jotting #2: Nulls and NullPointerExceptions

2007-06-02

Debugging NullPointerExceptions in Java can be a real pain since the stack trace provides you only with a line number and the error message null. Yep, just null. Very short and very helpful!

In some cases it’s obvious which object was null, but in others you have several objects, explicit or implicit, in the same statement, and now it is much less obvious. Even the line number is wrong since it refers to the line where the statement starts while the error occurs two lines later:

// an exaggerated code snippet:
Bar b = x.foo1( y.blah(), z.some(), ... )
         .foo2( ... )
         .foo3( w.crash() );

So what failed? Is it y or z that is null or is it the result of foo1 or foo2 or was it w? In these cases I wish that null were a proper object, a sub-class of all classes, that would throw an exception with the message like “foo1 invoked on null“.

Some languages do implement null as a special object of this kind: Eiffel, Ruby. It can’t be too hard to add this to Java. Till then, we’ll still need the debugger …