As this is meant as a somewhat academic question regarding the behaviour of the try/finally clause, I tried to use an example that is very generic. Is there any danger in nesting a try/finally clause like this?
openDatabaseConnection();
try {
// Methods unrelated to cursor
// ...
String cursor_id = openCursor();
try {
useCursor(cursor_id);
} finally {
closeCursor(cursor_id);
}
// Methods unrelated to cursor
// ...
} catch (Exception e) {
genericLogError();
} finally {
closeDatabaseConnection();
}
Specifically, I'm interested to know whether or not closeCursor()
is guaranteed to be called before closeDatabaseConnection()
. Is there reason nesting a finally clause like should be considered bad practice?
Yes, it is guaranteed.
Say some exception occurred during useCursor(cursor_id)
, now the inner finally block will be executed. (Since the exception happened in the inner try
block). After this genericLogError()
will be called (Since an exception occurred in the outer try
that included the inner try
. And only after it closeDatabasConnection()
will be executed (the finally
of the outer try
). Here is a diagram that might explain it better:
(Exception in the inner try → finally of the inner try ) → this is considered an exception of the outer try → catch of the outer try → finally of the outer try.
You can test it by printing from the blocks.
But why not to do it like this?
try {
useCursor(cursor_id);
} catch(Exception e) {
genericLogError();
} finally {
closeCursor(cursor_id);
closeDatabaseConnection();
}
There is nothing wrong with this, assuming you need to nest the try-catch blocks like this in the first place. Maroun's answer would be better for the example you provided. An example where the approach you suggested would be more suitable might be if you have lots of local variables involved in the "Cursor" clean-up:
openDatabaseConnection();
try {
Cursor cursor = openCursor();
CursorHelper helper = new CursorHelper(cursor);
String cursor_id = cursor.getId();
String something_else = cursor.getSomethingElse();
try {
useCursor(cursor_id, something_else, helper);
} finally {
closeCursor(cursor_id, something_else, helper);
}
} catch (Exception e) {
genericLogError();
} finally {
closeDatabaseConnection();
}
The parent try block will catch the exception thrown by the nested one, but the nested finally
block will be called first. This keeps the scope of the cursor variables encapsulated within the first try/catch
block.
If you wanted to pull all this code into one try/catch
block you would have to declare some of your variables outside the block, which can start to affect readability:
openDatabaseConnection();
CursorHelper helper = null;
String cursor_id = null;
String something_else = null;
try {
Cursor cursor = openCursor();
helper = new CursorHelper(cursor);
cursor_id = cursor.getId();
something_else = cursor.getSomethingElse();
useCursor(cursor_id, something_else, helper);
} catch (Exception e) {
genericLogError();
} finally {
if (cursor_id != null) {
closeCursor(cursor_id, something_else, helper);
}
closeDatabaseConnection();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With