Java Language Pitfall - Using exceptions for normal flowcontrol


Example

There is a mantra that some Java experts are wont to recite:

"Exceptions should only be used for exceptional cases."

(For example: http://programmers.stackexchange.com/questions/184654 )

The essence of this is that is it is a bad idea (in Java) to use exceptions and exception handling to implement normal flow control. For example, compare these two ways of dealing with a parameter that could be null.

public String truncateWordOrNull(String word, int maxLength) {
    if (word == null) {
        return "";
    } else {
        return word.substring(0, Math.min(word.length(), maxLength));
    }
}

public String truncateWordOrNull(String word, int maxLength) {
    try {
        return word.substring(0, Math.min(word.length(), maxLength));
    } catch (NullPointerException ex) {
        return "";
    }
}

In this example, we are (by design) treating the case where word is null as if it is an empty word. The two versions deal with null either using conventional if ... else and or try ... catch. How should we decide which version is better?

The first criterion is readability. While readability is hard to quantify objectively, most programmers would agree that the essential meaning of the first version is easier to discern. Indeed, in order to truly understand the second form, you need to understand that a NullPointerException cannot be thrown by the Math.min or String.substring methods.

The second criterion is efficiency. In releases of Java prior to Java 8, the second version is significantly (orders of magnitude) slower than the first version. In particular, the construction of an exception object entails capturing and recording the stackframes, just in case the stacktrace is required.

On the other hand, there are many situations where using exceptions is more readable, more efficient and (sometimes) more correct than using conditional code to deal with "exceptional" events. Indeed, there are rare situations where it is necessary to use them for "non-exceptional" events; i.e. events that occur relatively frequently. For the latter, it is worth looking at ways to reduce the overheads of creating exception objects.