I wonder if the following two swnippest are semantically identical, and, if not, what are the differences (we assume that we want to compute a result of type R, and want to guard against exception X that may be thrown in the course of doing so):
public R tcf(....) {
try {
R some = ...;
... compute the result ....
return some;
}
catch (X exception) {
... exception handling ....
}
finally {
... clean up ....
}
}
and the following:
public R tc(....) {
try {
R some = ...;
... compute the result ....
return some;
}
catch (X exception) {
... exception handling ....
}
}
public R tf(....) {
try {
return tc(....); // wrap the try-catch in tc()
}
finally {
... clean up ....
}
}
As far as I can see, it boils down to if a try-catch block wrapped in a try block with finally is the same, semantically, as a try-catch-finally block, assuming that the code in the finally and catch phrases stays the same and the outer try block just promotes the result of the inner one.
Practical relevance: Given a code base that does not make use of try-catch-finally, when it should, and given that for some reason, one cannot toch that code, one could more or less mechanically generate a layer of wrapper methods that add the finally.
I am fully aware of the fact, that for many reasons, one should use the try ... catch ... finally whenever possible. Specifically, I am not suggesting in any way that one should refactor the first example so that it looks like the second.
Rather, I want to make sure that example 2 can safely be refactored to example 1.
Functionally, they are equivalent.
I think it's good style to perform cleanup in the same method that has allocated the resources in question. In some cases this is the only practical way to go about things (for example, if the cleanup involves variables that are local to tcf
/tc
).
Also, if tc
doesn't clean up after itself, the cleanup becomes part of the function's contract, as a caller's obligation. This makes the design more error-prone, since the cleanup is easy to forget or get wrong. Additionally, if the steps involved in the cleanup in any way change, every single caller would need to be tracked down and updated.
Bottom line: if the cleanup can be performed in tcf
, it should be.
They are the same, however. The try/finally should always be right after the recourse (that finally releases) is claimed. (note that this happens just before the try)
For example:
getLock();
try {
doSomething()
}
finally {
releaseLock();
}
Because it is really easy to forget to clean up, it should always happen at the same location as the taking of the resource. Reason for this is that you should never burden your callers with something they always have to do (and you can do yourself)
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