Why does changing the returned variable in a finally block not change the return value?
Asked Answered
A

7

148

I have a simple Java class as shown below:

public class Test {

    private String s;

    public String foo() {
        try {
            s = "dev";
            return s;
        } 
        finally {
            s = "override variable s";
            System.out.println("Entry in finally Block");  
        }
    }

    public static void main(String[] xyz) {
        Test obj = new Test();
        System.out.println(obj.foo());
    }
}

And the output of this code is this:

Entry in finally Block
dev  

Why is s not overridden in the finally block, yet control printed output?

Ancylostomiasis answered 16/4, 2013 at 7:10 Comment(14)
obj.foo() return the value of s;Pearson
you should put the return statement on the finally block, repeat with me, finally block is always executedAssessor
in try block it return sPearson
The order of statements matters. You are returning s before you change it's value.Stirps
@Juvanis it's working properly and running.Pearson
But, you can return the new value in the finally block, unlike in C# (which you cannot)Koblenz
@AlvinWong yes we can return in finally,It gives some warning.... finally block does not complete normallyPearson
Maybe this is just you missed, but get in the habit of writing class names starting with upper case all the time.Predestinate
Related: does return "happen after" finallyKoblenz
Apart from java being able to work that way, I don't think it is a good coding practice to use mechanisms that arent easily understood (and work differently in many languages). Many people (like myself) will assume a return leaves the method and not execute the finally block. So why not just avoid such design and avoid any misunderstandings? just put the return even below your finally block.Disapproval
@Disapproval read this java doc docs.oracle.com/javase/tutorial/essential/exceptions/…Pearson
@dev you are right, i can see the benefits now. Just not sure I like it in most situations. But tnx for the info!Disapproval
Possible duplicate of Behaviour of return statement in catch and finallyCadmann
@PeterMortensen question as you pointed out, only one part of this question return with finally,but here another thing is that why "s" not overwrite by the compiler in finally block.Pearson
B
168

The try block completes with the execution of the return statement and the value of s at the time the return statement executes is the value returned by the method. The fact that the finally clause later changes the value of s (after the return statement completes) does not (at that point) change the return value.

Note that the above deals with changes to the value of s itself in the finally block, not to the object that s references. If s was a reference to a mutable object (which String is not) and the contents of the object were changed in the finally block, then those changes would be seen in the returned value.

The detailed rules for how all this operates can be found in Section 14.20.2 of the Java Language Specification. Note that execution of a return statement counts as an abrupt termination of the try block (the section starting "If execution of the try block completes abruptly for any other reason R...." applies). See Section 14.17 of the JLS for why a return statement is an abrupt termination of a block.

By way of further detail: if both the try block and the finally block of a try-finally statement terminate abruptly because of return statements, then the following rules from §14.20.2 apply:

If execution of the try block completes abruptly for any other reason R [besides throwing an exception], then the finally 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).

The result is that the return statement in the finally block determines the return value of the entire try-finally statement, and the returned value from the try block is discarded. A similar thing occurs in a try-catch-finally statement if the try block throws an exception, it is caught by a catch block, and both the catch block and the finally block have return statements.

Briggs answered 16/4, 2013 at 7:13 Comment(4)
If i use StringBuilder class instead of String than append some value in finally block,its changes the return value.why?Pearson
@dev - I discuss that in the second paragraph of my answer. In the situation you describe, the finally block does not change what object is returned (the StringBuilder) but it can change the internals of the object. The finally block executes before the method actually returns (even though the return statement has finished), so those changes occur before the calling code sees the returned value.Briggs
try with List, you get same behavior like StringBuilder.Catch
@yogeshprajapati - Yep. The same is true with any mutable return value (StringBuilder, List, Set, ad nauseum): if you change the contents in the finally block, then those changes are seen in the calling code when the method finally exits.Briggs
E
67

Because the return value is put on the stack before the call to finally.

Endlong answered 16/4, 2013 at 7:12 Comment(6)
That's true, but it doesn't address the OP's question as to why the returned string didn't change. That has to do with string immutability and references versus objects more than pushing the return value on the stack.Transsonic
@Transsonic even if String were mutable, using = would not mutate it.Tinner
@Tinner - I think you are confusing variables with memory objects they refer. = does not mutate a string, it assigns a reference to a different memory object. Mutability is a property of values (garbage collected memory objects), not variables referring to them.Settler
@Settler - Owen's point (which is correct) was that immutability has nothing to do with why OP's finally block did not affect the return value. I think that what templatetypedef might have been getting at (although this isn't clear) is that because the returned value is a reference to a immutable object, even changing the code in the finally block (other than using another return statement) could not affect the value returned from the method.Briggs
@TedHopp - Ugh.. apparently I misread Owen's comment. For some strange reason I remember seeing him asking rhetorically that if String was mutable then why would = mutate it. Hence the comment.Settler
@Transsonic No it doesn't. Nothing to do with String immutability whatsoever. Same would happen with any other type. It has to do with evaluating the return-expression before entering the finally-block, in other words exacfly 'pushing the return value on the stack'.Bul
C
33

If we look inside bytecode, we'll notice that JDK has made a significant optimization, and foo() method looks like:

String tmp = null;
try {
    s = "dev"
    tmp = s;
    s = "override variable s";
    return tmp;
} catch (RuntimeException e){
    s = "override variable s";
    throw e;
}

And bytecode:

0:  ldc #7;         //loading String "dev"
2:  putstatic   #8; //storing it to a static variable
5:  getstatic   #8; //loading "dev" from a static variable
8:  astore_0        //storing "dev" to a temp variable
9:  ldc #9;         //loading String "override variable s"
11: putstatic   #8; //setting a static variable
14: aload_0         //loading a temp avariable
15: areturn         //returning it
16: astore_1
17: ldc #9;         //loading String "override variable s"
19: putstatic   #8; //setting a static variable
22: aload_1
23: athrow

java preserved "dev" string from being changed before returning. In fact here is no finally block at all.

Carouse answered 16/4, 2013 at 10:8 Comment(1)
This is not an optimization. This is simply an implementatiom of the required semantics.Bul
D
22

There are 2 things noteworthy here:

  • Strings are immutable. When you set s to "override variable s", you set s to refer to the inlined String, not altering the inherent char buffer of the s object to change to "override variable s".
  • You put a reference to the s on the stack to return to the calling code. Afterwards (when the finally block runs), altering the reference should not do anything for the return value already on the stack.
Denadenae answered 16/4, 2013 at 7:16 Comment(4)
so if i take stringbuffer than sting will be overridden??Pearson
@dev - If you were to change the content of the buffer in the finally clause, that would be seen in the calling code. However, if you assigned a new stringbuffer to s, then the behavior would be the same as now.Briggs
yes if i append in string buffer in finally block changes reflect on output.Pearson
@Denadenae you also give very good answer and concepts,i highly thank full to you.Pearson
B
13

I change your code a bit to prove the point of Ted.

As you can see in the output s is indeed changed but after the return.

public class Test {

public String s;

public String foo() {

    try {
        s = "dev";
        return s;
    } finally {
        s = "override variable s";
        System.out.println("Entry in finally Block");

    }
}

public static void main(String[] xyz) {
    Test obj = new Test();
    System.out.println(obj.foo());
    System.out.println(obj.s);
}
}

Output:

Entry in finally Block 
dev 
override variable s
Bytom answered 16/4, 2013 at 7:22 Comment(3)
why overridden string s not return.Pearson
As Ted and Tordek already did say "the return value is put on the stack before the finally is executed"Bytom
While this is good additional information, I'm reluctant to upvote it, because it does not (on its own) answer the question.Caiaphas
R
5

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 s, 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.

Ream answered 16/4, 2013 at 7:23 Comment(1)
It is dubious why? It is congruent with evaluation order in all other contexts.Bul
D
0

Try this: If you want to print the override value of s.

finally {
    s = "override variable s";    
    System.out.println("Entry in finally Block");
    return s;
}
Dialectical answered 16/4, 2013 at 7:15 Comment(1)
it gives warning.. finally block does not complete normallyPearson

© 2022 - 2024 — McMap. All rights reserved.