Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to nest try/finally clauses like this?

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?

like image 462
Stephan B Avatar asked Mar 18 '13 09:03

Stephan B


2 Answers

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 trycatch of the outer tryfinally 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();
}
like image 103
Maroun Avatar answered Sep 29 '22 11:09

Maroun


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();
}
like image 21
seanhodges Avatar answered Sep 29 '22 09:09

seanhodges