Extending Java finally
to allow access to a Throwable

Dave Yost
2003-10-18
(updated to final 2nd Ed. JLS 2004-09-28)

Motivation

1.  For debugging purposes, it would sometimes be nice to be able to write code that sits in a finally clause and peeks at signals, if any, as they go by. A classic example is found in the JLS itself (below).

2.  Sometimes you want to bulletproof code against anything that might go wrong, for example, in a transaction situation, like this:

    boolean success = false;
    try {
        doSomething();
        success = true;
    } finally {
        if (!success) {
            transaction.rollback();
        } else {
            transaction.commit();
        }
    }

This approach has two problems: the use of a temporary variable to direct program flow is clutter, and the finally code has no way to know which Throwable is involved, in case it’s interested.

Proposal example

Here is the same functionality using the proposed optional, extended syntax:

    try {
        doSomething();
    } finally (Throwable th) {
        if (th != null) {
            transaction.rollback();
            System.err.println("Transaction rolled back: " + th);
        } else {
            transaction.commit();
        }
    }

There is another workaround, but it is lame:

    try {
        doSomething();
    } catch (Throwable th) {
        handleTheTrouble(th);
        throw th;   
    }

Now you’ve got to declare that the method throws Throwable, which really messes up the works, or you have to wrap the Throwable in some other exception and possibly have to declare that exception in the throws clause of the enclosing method.

Proposal

A small extension to the Java language would solve the problem nicely:

After the finally keyword, a (Throwable variable) declaration is optional.

If you provide the optional declaration, you must use Throwable as the type. The foregoing is motivated by the keep-it-simple principle. If you want to distinguish which subype of Throwable happened, you can use a standard instanceof if-else chain within the finally clause. (As before, and unlike catch, you can have only a single finally clause, not one for every exception type you want to match.)

Example:

    try {
        doSomething();
    } finally (Throwable th) {
        if (th != null) {
            handleTheTrouble(th);
        }
    }

When the new feature is used, just as in a traditional finally clause, the Throwable is not caught; moreover, the code in the finally clause has access to the Throwable via the declared variable. If the try block exited normally, the value of the variable is null.

Impact

This feature seems to require no change to the JVM spec, only a small compiler change.

JLS modifications

Below are excerpts from the JLS modified to accommodate the proposal. Once I was in there, I found the need for a lot of rewriting; only the substantive changes are highlighted. It is interesting to note that the example in the JLS cries out for the proposed language extension.

14.19 The try statement

...

TryStatement:
	try Block Catches
	try Block Catchesopt FinallyClause

Catches:
	CatchClause
	Catches CatchClause

TryHandler:
	CatchClause
	ParameterizedFinallyClause

CatchClause:
	catch ExceptionParameter Block

FinallyClause:
	SimpleFinallyClause
	ParameterizedFinallyClause

SimpleFinallyClause:
	finally Block

ParameterizedFinallyClause:
	finally ExceptionParameter Block

ExceptionParameter:
	( FormalParameter )

...

The Block immediately after the keyword try is called the try block of the try statement.

A try statement may have catch clauses (also called exception handlers). A catch clause must have exactly one exception parameter. The Block immediately after the exception parameter is called the catch block of the catch clause or can be called a catch block of the try statement. The declared type of a catch clause exception parameter must be the class Throwable or a subclass of Throwable, or a compile-time error occurs.

A try statement may have a finally clause. The Block of a finally clause is called the finally block of the finally clause or the finally block of the try statement. A finally clause may be a simple finally clause or a parameterized finally clause. A simple finally clause is simply be the keyword finally followed by a Block. A parameterized finally block is the keyword finally followed by exactly one exception parameter, followed by a Block. The declared type of a parameterized finally clause exception parameter must be the class Throwable, or a compile-time error occurs.

A try handler is a generic term which can be used to refer to a catch clause or to a parameterized finally clause.

An exception parameter variable of a try handler must not have the same name as a local variable or parameter of the method or initializer Block immediately enclosing the try handler, or a compile-time error occurs.

The scope of a try handler exception parameter is the entire try handler Block.

Within a try handler Block, the name of the exception parameter may not be redeclared as a local variable of the directly-enclosing method or initializer Block, nor may it be redeclared as an exception parameter of a try handler in a try statement of the directly enclosing method or initializer Block, or a compile-time error occurs. However, an exception parameter may be shadowed (§6.3.1) anywhere inside a class declaration nested within the try handler Block.

It is a compile-time error if an exception parameter that is declared final is assigned to within its try handler’s Block.

Exception parameters cannot be referred to using qualified names (§6.6), only by simple names.

Exception handlers are considered in left-to-right order: the earliest possible catch clause accepts the exception, receiving as its actual argument the thrown exception object.

A finally clause ensures that the finally block is executed after the try block and any catch block that might be executed, no matter how control leaves the try block or catch block.

A parameterized finally block executes with its actual parameter set either to the thrown exception object or to null if no exception was thrown.

...

14.19.2 Execution of try-catch-finally

...

class BlewIt extends Exception {
	BlewIt() { }
	BlewIt(String s) { super(s); }
}
class Test {
	static void blowUp() throws BlewIt {
		throw new NullPointerException();
	}
	public static void main(String[] args) {
		try {
			blowUp();
		} catch (BlewIt b) {
			System.out.println("BlewIt");
		} finally (Throwable th) {
			if (th != null) {
				System.out.println("Uncaught Exception " + th);
			}
		}
	}
}
produces the output:
Uncaught Exception java.lang.NullPointerException
java.lang.NullPointerException
	at Test.blowUp(Test.java:7)
	at Test.main(Test.java:11)

The NullPointerException (which is a kind of RuntimeException) that is thrown by method blowUp is not caught by the catch clause in the try statement in main, because a NullPointerException is not assignable to a variable of type BlewIt. The finally block inevitably executes, and the finally clause’s th variable is set to the exception object, which is printed if it is non-null. After this, the thread executing main, which is the only thread of the test program, terminates because of an uncaught exception, which typically results in printing the exception name and a simple backtrace.


http://Yost.com/computers/java/finally-throwable.html - this page
Previous version
2003-10-18 Created
2004-09-28 Modified