3

I am reading about final variables and learnt that when you have a parameter as final, you cannot change it. But then I tried out something

Compiler complains here saying you cannot change final parameter which I can understand

public class FinalVariableTest {

    public void method1(final FinalVariableTest object){
        object = new FinalVariableTest(); //Not allowed, compiler complains
    }

    public void method2(FinalVariableTest object){
        object = new FinalVariableTest();
    }

    public static void main(String[] args) {
        FinalVariableTest test = new FinalVariableTest();
        test.method1(test);
    }
}

But the compiler is fine with this

public class FinalVariableTest {

    public void method1(final FinalVariableTest object){
        method2(object);
    }

    public void method2(FinalVariableTest object){
        object = new FinalVariableTest(); //Compiler does not complain
    }

    public static void main(String[] args) {
        FinalVariableTest test = new FinalVariableTest();
        test.method1(test);
    }
}

What is the reason behind this?

Sneh
  • 3,527
  • 2
  • 19
  • 37
  • That's a different variable, and it's not declared as final. Why **shouldn't** you be able to change it? – azurefrog Oct 16 '15 at 21:54
  • @azurefrog but isn't it the case that I am passing the same variable to another method. – Sneh Oct 16 '15 at 21:56
  • @Sneh No, you are passing the **value** of the variable to another method, not the variable itself. In this case, that value is an object reference, so the second variable will refer to the same `FinalVariableTest` object, but you are still passing data by value, and the two variables are entirely distinct, since they are in different scopes. – azurefrog Oct 16 '15 at 22:03
  • 3
    @ochi NO, java always passes parameters by value – Sleiman Jneidi Oct 16 '15 at 22:06
  • 2
    [Here's a good blog post](http://javadude.com/articles/passbyvalue.htm) that talks more about this. It explains how Java is pass-by-value, compares it to how pass-by-reference languages work, and explains where the confusion comes from. – azurefrog Oct 16 '15 at 22:07
  • @azurefrog I got what you mean, thanks. So effectively if I have a long method chaining, I should keep declaring finals for parameter. – Sneh Oct 16 '15 at 22:09
  • Well, you can, but I don't know why you would. Just don't try to re-assign your parameters. If you're worried about doing it by accident, change your compilers settings to make doing so an error instead of a warning. – azurefrog Oct 16 '15 at 22:11
  • Remember, making the variable final makes it so that you cannot reassign the variable to point to a different object, it doesn't protect the object itself from modification. – azurefrog Oct 16 '15 at 22:12
  • @azurefrog yeah I personally never used final but I recently read at some places that one should use final keyword, it is a good practice and hence. And this is something which is eating my brain that why should I use final in parameter when I know I won't change it. I saw it in effective java's builder pattern where they were passing finals everywhere. – Sneh Oct 16 '15 at 22:12
  • @azurefrog yep I know about that. – Sneh Oct 16 '15 at 22:13
  • http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value – Petter Friberg Oct 16 '15 at 22:46

2 Answers2

5

The keyword final means that you cannot change the reference held in the variable to another object (primitive types aside), not that you cannot change the referenced object itself.

In your example, you cannot change what object variable in method1() is pointing at, but you can indeed change what the object variable in method2() is pointing at, since that one isn't final. And the whole time, you're free to change the object being referenced / pointed at, unless that object itself has protections against it.

final method1.object -- cannot point elsewhere
             \                    method2.object -- not final, can be switched to here --.
              \                  /                                                       |
               \                /                                                        |
           original FinalVariableTest instance                       new FinalVariableTest()
Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
  • So you mean after calling method2() from method1(), my original instance will start pointing to a new location even when I explicitly defined in method1() that the parameter is final.? – Sneh Oct 16 '15 at 22:02
  • 1
    No it won't. If you assign a new object into `method2.object`, the `method1`'s `object` will still point to the original object. Java is passing references to objects, and passing them as values. Imagine you have the original object at address 0001, this address is held in your final `method1.object`. Then you call `method2()`, passing the address 0001 into `method2.object`. When you assign a new object at address 0002 to `method2.object`, it overwrites the original 0001 in `method2.object` with 0002 (it's not final, so it's OK), but doesn't change what's in `method1.object`. – Jiri Tousek Oct 16 '15 at 22:08
0

You are not changing the object in the scope of method1, only in the scope of method2. In that sense, after calling method2 in your code, the object in method1 will still be the original (final) object, not the one created in method2

Nir Levy
  • 12,750
  • 3
  • 21
  • 38