Finally blocks are underrated. They’re really very useful for removing duplication in code with multiple exit points. Consider the following:
public int foo(boolean condition1, boolean condition2) { setMouseBusy(); if (condition1) { doOne(); setMouseNormal(); return 1; } if (condition2) { doTwo(); setMouseNormal(); return 2; } setMouseNormal(); return 0; }
Compare with this:
public int foo(boolean condition1, boolean condition2) { try { setMouseBusy(); if (condition1) { doOne(); return 1; } if (condition2) { doTwo(); return 2; } return 0; } finally { setMouseNormal(); } }
Why is the second form better? Because no matter how the method exits (including throwing exceptions), the finally block will be executed. In the first form, if either of the ‘do’ calls throw an exception the call to setMouseNormal will be skipped, possibly leaving the application with a misleading hourglass cursor.
I agree with you about finally – they are great, but your code example has a serious problem: setMouseNormal() will be called AFTER the doOne()/doTwo()/doDefault() functions in the second example. In the first example it will be called BEFORE these functions. Could be OK, but could be totally bad as well.
You’re right. I changed the example slightly to fix it.
Isn’t a try block pretty expensive in terms of performance? There are other ways to refactor the code and remove the duplication. For example:
setMouseBusy();
int returnVal = 0;
if (condition1) {
doOne();
returnVal = 1;
}
if (condition2) {
doTwo();
returnVal = 2;
}
setMouseNormal();
return returnVal;
}
Try blocks themselves are not at all expensive. Actually throwing an exception is the expensive thing. One of the points of exceptions is to optimise the most common path through the code. Throwing an exception is expensive, but that’s why they’re called ‘exceptions’, they’re supposed to only show up in uncommon events.
The following is paraphrased from section 7-12 and 7-13 of the Java Virtual Machine spec.
Every method has an exception table, which is a list of entries of the form “Between instructions 5 and 8, the exception handler for FooException starts at instruction 12”. The table is only looked up if an exception is thrown. So if no exception is thrown, there is no performance difference between a try block and a regular block.
Finally blocks are compiled as a ‘jsr’ (jump to subroutine) instruction, and maybe a few gotos if the method has multiple exit points. Since all of these are just jumping around to different instructions inside the same method, the performance cost of this is infinitessimal.