finally
Throwable
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.
Here is the same functionality using the proposed optional, extended syntax:
try {
doSomething();
} finally (Throwable th) { // Note the Throwable declaration
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.
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
.
This feature seems to require no change to the JVM spec, only a small compiler change.
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.
try
statement...
TryStatement: try Block Catches try Block Catchesopt FinallyClause Catches: CatchClause Catches CatchClause TryHandler: CatchClause ParameterizedFinallyClause CatchClause: catch ExceptionParameter Block FinallyClause: finally Block ParameterizedFinallyClause 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 an exception parameter; the declared type of the exception parameter must be the class Throwable
or a subclass of Throwable
, or a compile-time error occurs. 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.
A try
statement may have a finally
clause. A finally
clause may simply be the keyword finally
followed by a Block, or it may be a parameterized finally
clause. A Block immediately after the keyword finally
is called the finally
block of the finally
clause or the finally
block of the try
statement.
A parameterized finally
clause has an exception parameter; the declared type of the exception parameter must be the class Throwable
, or a compile-time error occurs. The Block immediately after the exception parameter is called the finally
block of the finally
clause or the finally
block of the try
statement.
A try handler is a generic term which can be used to refer to a catch
clause or to a parameterized finally
clause.
The exception parameter variable of a try handler must not have the same name as a local variable or parameter variable of the method or initializer Block immediately enclosing the try handler, or a compile-time error occurs.
The scope of a try handler’s exception parameter variable is the entire handler’s Block.
Within a try handler Block, the name of the exception parameter variable 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 variable 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 variable 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 variable that is declared final is assigned to within its try handler’s Block.
Exception parameter variables cannot be referred to using qualified names (§6.6), only by simple names.
If an exception is thrown in the try Block, the try Block’s exception handlers are considered in left-to-right order, and the leftmost catch
clause whose type matches the exception object executes with its actual parameter set to the thrown exception object.
A finally
block is always executed after the try
Block and after 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.
...
...
produces the output: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); } } } }
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 tested for non-null
, then printed. 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.