Recently I was working on a "cleanup" method and I wanted to make sure that each statement is attempted to be executed. My first approach was something like this (where s#;
are statements):
try {
s1;
} finally {
try {
s2;
} finally {
s3;
}
}
For simplicity I have omitted the exception handling part here (the catch
blocks), where I store the first occurring and subsequent suppressed Throwable
s to rethrow later.
Then I noticed that there is another (maybe more correct?) way to do the same thing:
try {
try {
s1;
} finally {
s2;
}
} finally {
s3;
}
In my opinion the second approach feels more correct because as soon as s1;
is attempted, we are already inside both try
blocks, which guarantees that all finally
blocks will be executed.
My question is: Is there actually a difference between those approaches in terms of guarantees that the JVM provides?
Also is it theoretically possible that an Error
is thrown by the JVM in the first code example at // not a statement
(see code below), such that the inner try
-finally
block isn't attemped anymore?
try {
s1;
} finally {
// not a statement
try {
s2;
} finally {
s3;
}
}
According to 11.1.3. Asynchronous Exceptions, exceptions can be thrown basically anywhere. This is the main reason why I think that nesting the try
-finally
blocks is necessary as shown in the second code example. Or do I understand the JLS wrong and exceptions can only occur if there is also a statement?
Add an
s4
and ans5
into the mix and it becomes even clearer than it already should be that this is a hard to maintain mess that requires comments explaining why you've got this bizarro mix of nested try blocks.Hence, I suggest you do this instead:
You can fancy that up some and replace
t = e;
with a utility method (t = update(t, e);
) which, if an exception has already occurred, will attach them as suppressed exceptions just like how try-finally does it.Or, even nicer, add lambda support:
To use:
It is not.