Suppose I have a lazy iterable instance which requires closing to free resources. If I return this from a method and close it in a finally block, will the finally block be invoked before or after it is consumed by the client?
For example:
public CloseableIterable<T> getStuff() {
CloseableIterable<T> iterable = fetchStuffFromSomewhere();
try {
return iterable;
} finally {
iterable.close();
}
}
Note: on Java 6 here, but I'd also be interested in how the try-with-resources bits in Java 7 would handle this case.
The finally
block will always execute before the try
block is considered finished.
According to the JLS, Section 14.17, a return statement
attempts to transfer control to the invoker of the method that contains it;
and
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.
(bold emphasis mine)
That is, a return
statement inside a try
will execute the finally
block before actually returning. Therefore, your CloseableIterable
will be closed before it gets returned to the caller.
As for "try-with-resources", the code is equivalent to a try-catch-finally block, so it should behave the same. According to the JLS, Section 14.20.3,
A try-with-resources statement is parameterized with variables (known as resources) that are initialized before execution of the try block and closed automatically, in the reverse order from which they were initialized, after execution of the try block.
That is, the equivalent code you've written above with "try-with-resources" in Java 1.7 would be:
try (CloseableIterable<T> iterable = fetchStuffFromSomewhere()) {
return iterable;
}
... and it would still close it before control is transferred back to the method's caller.