Jumping out of Java finally

The definition of the return statement in the current Java Language Specification (§14.17) features an innocuous-looking sentence:

It can be seen, then, that a return statement always completes abruptly.

Other statements that “always complete abruptly” include break (§14.15), continue (§14.16), and of course throw (§14.18). So what? Well, Java also has try-finally blocks (§14.20). As you would expect, exceptions thrown in a finally clause override any previous exception thrown in the associated try block. But this override is defined in terms of “abrupt completion” – and therefore applies to all jump statements!

Generally, the abrupt completion of a finally clause hides any previous abrupt completion of the associated try block. Let’s see what this means in practice.

Jump Hides Exception

Most importantly, simply returning from a finally clause will swallow any previous exception. The following method will never propagate the exception it throws:

static void neverThrows() {
    try {
        throw new RuntimeException();
    } finally {
        return;
    }
}

Dustin Marx has compiled some examples and links on this surprising behavior, on the occasion of a new warning message in NetBeans 7.4. You can also use the compiler option -Xlint:finally to warn against “abrupt completion” in finally clauses.

Jump Hides Return

You don’t even need to throw exceptions. Ordinary jump statements will hide each other when finally clauses are involved. Consider the following method:

static int neverPlusOne() {
    for(;;) {       	   
        try {
            return +1;
        } finally {
            break;	   
        }		   
    }		   
    return -1;
}

This method always returns -1, not +1, because the break in the finally clause overrides the early return +1 in the try block. The code snippet is from the Java Hall of Shame which also provides a continue example. As the end of JSL §14.18 quaintly states,

Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

The same caveats apply to finally blocks in JavaScript, by the way. C# avoids the whole mess by simply forbidding jumps out of finally blocks. I added a note on Java’s surprisingly different behavior to Java for C# Programmers.

Leave a Reply