Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Effect of "finally" block on return values from "try" block [duplicate]

Tags:

java

I was reading this question and I got the following code snippet:

public void testFinally(){
    System.out.println(setOne().toString());

}

protected StringBuilder setOne(){
    StringBuilder builder=new StringBuilder();
    try{
        builder.append("Cool");
        return builder.append("Return");
    }finally{
        builder.append("+1");
    }
}

The answer is: CoolReturn+1

Okay then I tried the same with String and int following is my code snippet with String:

public void testFinally(){
    System.out.println(setOne().toString());
}

protected String setOne(){
    String str = "fail";
    try{
        str = "success";
        return str;
    }finally{
        str = str + "fail";
    }
}

Why the answer is: success. Why not successfail, as in 1st case finally appends value and here I am doing concatenation?

Also I tried with primitive type int

public void testFinally(){
    System.out.println(setOne());
}

protected int setOne(){
    int value = 10;
    try{
        value  = 20;
        return value ;
    }finally{
        value  = value  + 10;
    }
}

Here too why the answer is: 20 why not 30.

like image 779
Vishrant Avatar asked Mar 31 '14 20:03

Vishrant


People also ask

What happens if you put a return statement in a try block will a Finally block be executed?

Yes, the finally block will be executed even after a return statement in a method. The finally block will always execute even an exception occurred or not in Java.

What will happen when try and finally block both return value?

When try and finally block both return value, method will ultimately return value returned by finally block irrespective of value returned by try block.

Can we return value in finally block?

Yes, we can write a return statement of the method in catch and finally block.

Does finally run if Try returns?

Answer is Yes, The finally block is executed even after a return statement in the method. So, finally block will always be executed even whether an exception is raised or not in java. We will look into the following in this article. Finally block is executed right after try or catch blocks.


1 Answers

TL;DR: You've already told the method what to return. The finally block happens after you've done that. Additionally, reference variables are just pointers to objects, and while the finally block can't change the reference itself, it can certainly change the object that the reference points to.

Long answer:

There are two things going on here:


First, you've already told the method what value to return. The JLS states it fairly simply, from 14.20.2 (emphasis mine):

A try statement with a finally block is executed by first executing the try block.

That is, the try block executes completely before the finally block is run. Also, from 14.7:

... execution of such a return statement first evaluates the Expression.

The implication is that any and all effects the try block had (including specifying the value that the method should return) are complete, and that the value to be returned has been completely evaluated and is now "set in stone", so to speak.

Let's concentrate on this first. Here, we look at a slightly modified version of your primitive example. The original example you posted isn't a great example, because the initial value 10 of the integer plus the 10 in the finally block also happens to equal 20, which clouds what is truly happening. So let's consider this instead:

protected int setOne(){
    int value = 5;
    try{
        value = 20;
        return value;
    }finally{
        value = value + 10;
    }
}

The return of this method is 20. Not 15, not 30, but 20. Why? Because in the try block, you set value = 20, then you tell the method to return 20; value is evaluated in the return statement, and its value at that time is 20. Nothing the finally block does can change the fact that you've already told the method to return 20.

Ok, easy.


Now the second thing that is happening, in your other examples, is that reference variables point to objects. That is, they are essentially primitive integer variables that hold the memory address of an object. They follow the same rules as primitive types above! Before we look at the rest of your examples, consider the following:

int array[] = new int[] { 100, 200, 300 };

int example () {
    int index = 1;
    try {
       return index;
    } finally {
       array[index] = 500;
       index = 2;
    }
}

This method returns 1, not 2 (for reasons explained above). The finally block also modifies array[1]. So what, then, does value contain after the following:

int index = example();
int value = array[index];

It's 500, of course. We can see this without much explanation being required. The example method returns an index into an array. The finally block modifies the data in the array. When we look at the data at that index later, we see it contains 500, because the finally block set it to 500. But changing the data in the array is unrelated to the fact that the returned index is still 1.

This is exactly the same as returning a reference. Think of a reference variable as a primitive integer that is essentially an index into a large array of memory (the heap). Modifying the object that a reference points to is like modifying data in that array. Now, the rest of the examples should make more sense.

So let's look at your first example:

protected StringBuilder setOne(){
    StringBuilder builder=new StringBuilder();
    try{
        builder.append("Cool"); // [1]
        return builder.append("Return"); // [2]
    }finally{
        builder.append("+1"); //[3]
    }
}

In your question, you've stated that you are confused because this method returns "CoolReturn+1". However, this statement fundamentally doesn't make much sense! This method is not returning "CoolReturn+1". This method is returning a reference to a StringBuilder that happens to contain the data, "CoolReturn+1".

In this example, first line [1] is evaluated. Then line [2] is evaluated, and .append("Return") is executed. Then the finally block happens, and line [3] is evaluated. Then, since you've already told the method to return a reference to that StringBuilder, that reference is returned. The StringBuilder pointed to by the returned reference has been modified by finally, and that's OK. That does not affect the value returned by the method, which is simply a reference to an object (i.e. the "index" into that big array of memory I described earlier).

Ok, so let's look at your second example:

protected String setOne(){
    String str = "fail";
    try{
        str = "success";
        return str;
    }finally{
        str = str + "fail";
    }
}

This returns a reference to a String that contains the data, "success". Why? For all the reasons already described above. This line:

str = str + "fail";

Simply creates a new String object that is the concatenation of the two strings, and then assigns str a reference to that new object. However, as with the primitive int example, we've already told the function to return a reference to the "success" String, and we cannot change that no matter what we do!


Conclusion:

You can come up with an infinite number of examples, but the rules will always be the same: The return value is evaluated in the return statement, and the value cannot be changed later. Reference variables are simply values that hold memory addresses of objects, and that memory address value cannot be changed, even though the object at that address can certainly be modified by finally.

Note also that immutability is irrelevant to the general concept here. In the String example it is a bit of a red herring. Keep in mind that even if Strings were mutable, we would never expect a binary + operator to modify the fields of its left operand (e.g. even if strings had, say, an append() method, a = a + b would not be expected to modify any fields of a, it would be expected to return a new object then store a reference to that in a, leaving the original a untouched). One source of confusion here is that Java allows + on String as a convenience; no other objects directly support operators like that (not counting automatic unboxing of primitive wrappers).

I did mark this question as a duplicate of Why does changing the returned variable in a finally block not change the return value?. I believe that, once you have your head wrapped around the concepts here, it will be clear that the question and answer there are essentially the same as the question and answers here.

like image 109
Jason C Avatar answered Sep 28 '22 04:09

Jason C