Java try-finally return design question
Technically speaking, the return
in the try block won't be ignored if a finally
block is defined, only if that finally block also includes a return
.
It's a dubious design decision that was probably a mistake in retrospect (much like references being nullable/mutable by default, and, according to some, checked exceptions). In many ways this behaviour is exactly consistent with the colloquial understanding of what finally
means - "no matter what happens beforehand in the try
block, always run this code." Hence if you return true
from a finally
block, the overall effect must always to be to return true, no?
In general, this is seldom a good idiom, and you should use finally
blocks liberally for cleaning up/closing resources but rarely if ever return a value from them.
try{} finally{} construct with return values
Everything works exactly as expected, no bugs here. When you have doubts, the JLS is your savior:
JLS - 14.20.2. Execution of try-finally and try-catch-finally:
If execution of the
try
block completes abruptly for any other
reason R, then thefinally
block is executed, and then there is a
choice:
If the finally block completes normally, then the try statement
completes abruptly for reason R.If the finally block completes abruptly for reason S, then the try
statement completes abruptly for reason S (and reason R is
discarded).
It overrides the value in the try
block.
return
inside finally
discards all exceptions that can be thrown in try
clause.
Using try-finally to execute statements after return
Without the finally
you declare indeed an additional Foo
variable.
But is it really expensive ? Not as in both cases the Foo
object exists in memory. You just added a reference to access to it.
A reference to an object in the scope of a method is really cheap.
You should never worry about that.
Besides, you don't have to use finally
statement as a way to improve the performance of the executed code.
Readers of the code will never guess such a thing.finally
serves to :
The finally block always executes when the try block exits. This
ensures that the finally block is executed even if an unexpected
exception occurs.
and
putting cleanup code in a finally block is always a good practice,
even when no exceptions are anticipated.
The first code without the finally
statement is much clearer and doesn't have any reading indirection.
So I advise to stick to :
Foo result = array[index];
index = (index + 1) % array.length;
return result;
Try-catch-finally-return clarification
If the return
in the try
block is reached, it transfers control to the finally
block, and the function eventually returns normally (not a throw).
If an exception occurs, but then the code reaches a return
from the catch
block, control is transferred to the finally
block and the function eventually returns normally (not a throw).
In your example, you have a return
in the finally
, and so regardless of what happens, the function will return 34
, because finally
has the final (if you will) word.
Although not covered in your example, this would be true even if you didn't have the catch
and if an exception were thrown in the try
block and not caught. By doing a return
from the finally
block, you suppress the exception entirely. Consider:
public class FinallyReturn {
public static final void main(String[] args) {
System.out.println(foo(args));
}
private static int foo(String[] args) {
try {
int n = Integer.parseInt(args[0]);
return n;
}
finally {
return 42;
}
}
}
If you run that without supplying any arguments:
$ java FinallyReturn
...the code in foo
throws an ArrayIndexOutOfBoundsException
. But because the finally
block does a return
, that exception gets suppressed.
This is one reason why it's best to avoid using return
in finally
.
try-catch-finally with return after it
The fact that the finally block is executed does not make the program forget you returned. If everything goes well, the code after the finally block won't be executed.
Here is an example that makes it clear:
public class Main {
public static void main(String[] args) {
System.out.println("Normal: " + testNormal());
System.out.println("Exception: " + testException());
}
public static int testNormal() {
try {
// no exception
return 0;
} catch (Exception e) {
System.out.println("[normal] Exception caught");
} finally {
System.out.println("[normal] Finally");
}
System.out.println("[normal] Rest of code");
return -1;
}
public static int testException() {
try {
throw new Exception();
} catch (Exception e) {
System.out.println("[except] Exception caught");
} finally {
System.out.println("[except] Finally");
}
System.out.println("[except] Rest of code");
return -1;
}
}
Output:
[normal] Finally
Normal: 0
[except] Exception caught
[except] Finally
[except] Rest of code
Exception: -1
Related Topics
How to Determine the Primitive Type of a Primitive Variable
Convert Xml to Java Object Using Jaxb (Unmarshal)
How to Use Argumentcaptor for Stubbing
How to Save User Settings in Java Application
Copy Entire Directory Contents to Another Directory
How to Convert "Mon Jun 18 00:00:00 Ist 2012" to 18/06/2012
@Valid Annotation Is Not Validating the List of Child Objects
Null Pointer Exception While Using Java Compiler API
Pdfbox Encode Symbol Currency Euro
How to Set Eclipse Console Locale/Language
Rejectedexecutionexception Inside Single Executor Service
Java.Rmi.Connectexception: Connection Refused to Host: 127.0.1.1;
Access "This" from Java Anonymous Class
Jackson Dynamic Property Names
How to Programmatically Inject a Java Cdi Managed Bean into a Local Variable in a (Static) Method