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) {
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: 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.
...
...
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 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.