Why Does Changing the Returned Variable in a Finally Block Not Change the Return Value

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

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.

Why does returning a variable in a try block not change the value of the returned thing when this variable is reset in the finally section?

Because when the return instruction is executed, it PUSHs in the CPU Stack the value to be returned.

Then the finally block is executed but it does not modify the value already pushed.

Thus after the method PROC RET, the caller POPs the value and has what it has been PUSHed, but the data itself has been reseted.

Therefore, calling the method again will return 0.

This means that the return statement is executed first, and the code in finally is executed after, so the result is previously stored and changing data does not change this stored in the stack result.

try-finally (C# Reference)

We can check that using for example ILSpy:

.method public hidebysig 
instance int32 Retrieve () cil managed
{
// Method begins at RVA 0x4cf4
// Code size 30 (0x1e)
.maxstack 1
.locals init ( [0] int32 )

.try
{
// Inc();
IL_0002: ldarg.0
IL_0003: call instance void ConsoleApp.Container::Inc()

// return data;
IL_0009: ldarg.0
IL_000a: ldfld int32 ConsoleApp.Container::data
IL_000f: stloc.0

IL_0010: leave.s IL_001c
} // end .try
finally
{
// Reset();
IL_0013: ldarg.0
IL_0014: call instance void ConsoleApp.Container::Reset()
// }
IL_001b: endfinally
} // end handler

IL_001c: ldloc.0
IL_001d: ret
} // end of method Container::Retrieve

OpCodes.Stloc_0 Field

OpCodes.ldloc_0 Field

// Console.WriteLine(new Container().Retrieve());
IL_000c: newobj instance void ConsoleApp.Container::.ctor()
IL_0011: call instance int32 ConsoleApp.Container::Retrieve()
IL_0016: call void [mscorlib]System.Console::WriteLine(int32)

OpCodes.Call Field

Will modifications to a function's return value in the finally block take effect?

The following things will happen when you will call this method:

  1. The expression, x = x + 1; will increase the value of x by 1 i.e. its value will become 1. Then, throw new Exception(); will be executed and as a result, the control will enter the catch block.
  2. Inside the catch block, x = x + 2; will increase the value of x by 2 i.e. its value will become 3 which is the value that will be returned.
  3. Since finally block is supposed to be executed irrespective of whether an exception is thrown or not, this block will be executed but it won't change the return value. Thus, finally block is useless here. Typically, finally block is used to release resources. Check this to learn more about it.

Finally always runs just before the return in try block, then why update in finally block not affect value of variable returned by try block?

I think the problem you have is more related to value assignment than what try and finally do. I suggest to read Facts and myths about Python names and values.

When you return a value, it just like assigning the value to a variable, result for example, and finally always execute to reassign the value. Then, your example code may be represent as:

# try 
result = True # return
# finally
result = False # return (reassign value)
print(result) # Output: False

# try
x = 100
result = x # return
# finally
x = 90
result = x # return (reassign value)
print(result) # Output: 90

# try
x = 100
result = x # return
# finally
x = 90 # no return so result not updated
print(result) # Output: 100
print(x) # Output: 90 (x is changed actually)

# try
x = [100]
result = x # return the list (result refer to a list and list is mutable)
# finally
x[0] = 90 # changing the list in-place so it affects the result
print(result) # Output: [90]

# try
x = [100]
result = x[0] # return the integer (result refer to the integer)
# finally
# changing the list in-place which have no effect to result unless reassign value by return x[0]
x[0] = 90
print(result) # Output: 100
print(x) # Output: [90] (x is changed actually)

Modifying a return value of a method in a finally block doesn't return the actual modified value

The key here is that you are returning a reference value to the respective object from your method. And in the caller side, what you get is the copy of the reference of the method. Remember, everything in Java are passed by value. Even the references are passed by value.

When you modify the object using the reference in the finally block, the changes will be visible on the caller side. But, if you change the value of the reference itself, then the changes would of course not be reflected on the caller side, as now both of them have a different references.

Before this method returns, the value of productName is modified in the finally block.

What you have done in the finally block is, you've assigned a new String object to the productName reference. That will change the the reference value of productName. And thus as per the above paragraph, the changes wouldn't be reflected on the caller end. Both of them are dealing with 2 different String objects.

Therefore, the method should return this new modified value.

No, it shouldn't. Because both the String objects are different alltogether. Try this code with a StringBuilder object:

public StringBuilder getProductName() {

StringBuilder sb = new StringBuilder(productName);
try {
return sb;
}
finally {
sb.append("Modified");
}
}

Now the changes to the StringBuilder will be reflected in the return value. So what is happening here?

First with the following return statement:

return sb;

you returned a reference to the StringBuilder object you created. Then in the finally block:

sb.append("Modified");

.. you've modified the object pointed to by sb. Since you haven't changed the reference value of sb itself, the changes to the object will be visible for all the references pointing to that object, and hence the one at the caller end too.

Now modify the finally block as:

sb = new StringBuilder("Modified");

and see whether the change is reflected or not. You will not see any change in return value. Because, now you have changed the reference value of sb itself.

Further, if you return the modified reference from the finally block, the original returned value would be overwritten. Try modifying your finally block to:

finally {

this.productName="The product name has been modified in the finally block.";
System.out.println("Product name in the finally block : "+this.productName);

return this.productName; // Note: This is bad idea
}

Now you will see the new value.


See also:

  • java: try finally blocks execution

Effect of finally block on return values from try block

The first and second methods return the reference to the object, the finally block is executed later, what happens in the first example is that you still keep the reference to the object (the builder) so you can modify it.

In the second example you have a string, which also is inmutable, so you are not able to modify it, only assign it a new object to the variable. But the object which you have returned is not modified. So when you do str = str + "fail"; you assign to your variable str a new object.

In the third example you have an integer, which is not an object, it returns it´s value, later in the finally block you assign your variable to a new integer, but the returned one is not modified

Detailed explanation:

Imagine a fourth case:

    public static class Container{
public int value = 0;
}

protected static Container setOne(){
Container container = new Container();
try{
container.value = 20;
return container ;
}finally{
container.value = container.value + 10;
}
}

This function retrieves the reference to a variable called container, and after returning it it increments the container's value field to +10, so when you exit the function, the container.value will be at 30, just like in the StringBuilder example.

Let´s compare this method with the third example (the int method):

If you get the bytecode of this two methods you get:

For the example of the int:

bipush 10
istore_0
bipush 20
istore_0
iload_0
istore_2
iinc 0 10
iload_2
ireturn <- return point
astore_1
iinc 0 10 <- retrieve the variable value and add 10 to it's value
aload_1 <- Store the value of the result of the sum.
athrow

For the example of the Container wrapper class:

new Test4$Container
dup
invokespecial Test4$Container/<init>()V
astore_0
aload_0
bipush 20
putfield Test4$Container/value I
aload_0
astore_2
aload_0
aload_0
getfield Test4$Container/value I
bipush 10
iadd
putfield Test4$Container/value I
aload_2
areturn <-- Return point
astore_1 <-- Stores the reference
aload_0
aload_0
getfield Test4$Container/value I <-- gets the value field from the object reference
bipush 10
iadd <-- add the value to the container.value field
putfield Test4$Container/value I <-- Stores the new value (30) to the field of the object
aload_1
athrow

As you can see, in the second case the finally statement access to the referenced variable incrementing it's value. but in the int example it only adds 10 to the value of the variable, assigning a new value to the variable.

I've used this example because it's bytecode is easier to be read than the stringbuffer one, but you can do it with it and you will have a similar result.

The order of return value with try catch finally

This question is closely related, although it returns literals but not variables: Multiple returns: Which one sets the final return value?

In fun1 the return value is set via return a in the catch-block. At that line the value of a is copied into the return value. Changing a later does not change the return value.

In fun2 you have an explicit return in the finally block, so the return value in the finally block is what is returned.

Please read carefully through the answers in the question above for why you should not write code like that.

Another related question is this one: Returning from a finally block in Java

Can a ‘finally’ block alter a returned value from its ‘try’ block?

I'll step you through it with code comments.

//This reserves a memory location to store a reference to an ArrayList
private static ArrayList obj;

public static ArrayList GetObj()
{
try
{
//This instantiates a new ArrayList and stores a reference to it in obj
obj = new ArrayList();

//This creates a copy of the reference stored in obj and returns it to the caller
return obj;
}
catch (Exception e)
{
throw e;
}
finally
{
//This sets the reference stored in obj to null. This does not affect the copy of the reference that was returned earlier
obj = null;
}
}

public static void SomeMethod()
{
//This reserves a different memory location that can store a reference to an ArrayList.
//Note that this exists in parallel with the static field named obj
ArrayList obj;

//This retrieves the reference to the ArrayList that was returned and stores it in the local (not static) variable obj
obj = Example.GetObj();
}

In the end, your local variable obj receives a copy of the ArrayList reference and is unaffected by the finally block.



Related Topics



Leave a reply



Submit