Miquel
Miquel

Reputation: 15675

Understanding Java's approximation to "closures"

So, here's what I understand: Java doesn't support closures, so it sort of copies the variables from the containing scope into the nested scope, so they are available later. Because this is a copy, there is no way to synchronize the original and the copy, and the variable is forced to be final so the developer cannot change it and expect it to be updated. This understanding is partly taken form these answers

And that brings us to this code working:

public class SimpleClosure {

    public static void main(String[] args) {
        new SimpleClosure().doStuff();
    }

    public void doStuff() {
        final int number = 3;

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Integer.toString(number));
            }
        }.start();
    }
}

So, great. Now, the thing is, a final modifier only prevents me from changing the object that the variable points to, but I can change the object without problems. If a "copy" was made, then changes to the object contents should not be reflected. The question, therefore is, why does the following code work?

import java.util.HashMap;
import java.util.Map;

public class StretchingClosure {

    public static void main(String[] args) {
        new StretchingClosure().doStuff();
    }

    public void doStuff() {
        final Map<String, String> map = new HashMap<String, String>();
        map.put("animal", "cat");

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(map.get("animal")); // Outputs dog See (1)
            }
        }.start();
        map.put("animal", "dog");
    }
}

Needless to say, I'm missing something, or I'm oversimplyfying the way the compiler handles these cases. Could anyone please enlighten me?

(1): as pointed out by @trashgod, the output is true most of the time on most of the platforms, but it's not guaranteed due to the lack of synchronization. This is good enough for the example, but bad practice in general.

Upvotes: 4

Views: 210

Answers (4)

Bastien Jansen
Bastien Jansen

Reputation: 8846

Anonymous classes do not get copies of variables, but rather copies of references to the objects, that's why after 1s you get the "right" value which was changed outside the anonymous class.

Upvotes: 0

eis
eis

Reputation: 53482

Java does the same thing there as it does with regular method parameters:

Method parameters are passed by reference value, so while you cannot change the object itself, if it is mutable and provides ways of mutating its inner state, you can change that state. You cannot change a string, but you can change the items inside a collection, for example.

The reference is passed by value = the reference is copied. The object itself isn't.

Upvotes: 1

trashgod
trashgod

Reputation: 205785

The comment // Outputs dog is misleading in the sense that it's only true most of the time on most platforms. One second is plenty of time to update the Map on the initial thread, but nothing guarantees visibility of the updated value in the anonymous thread unless access to the shared data is correctly synchronized. See Memory Consistency Properties for a nice summary of relevant features in java.util.concurrent.

Upvotes: 1

Marko Topolnik
Marko Topolnik

Reputation: 200168

Don't confuse the variable with the object: the reference from the local variable is indeed copied, but it still refers to the same object, in your case the map. There is a widely known idiom to work around the final restriction, involving arrays:

final int[] x = {1};
... use in an anonymous instance...
System.out.println(x[0]);

Upvotes: 4

Related Questions