I think I've met this classic situation in JavaScript.
Usually the programmer would expect this code below to print "Peter", "Paul", "Mary".
But it doesn't. Could anyone explain exactly why it works this way in Java?
This Java 8 code compiles OK and prints 3 times "Mary".
I guess it's a matter of how it's implemented deep down
but ... doesn't this indicate a wrong underlying implementation?
import java.util.List;
import java.util.ArrayList;
public class Test008 {
public static void main(String[] args) {
String[] names = { "Peter", "Paul", "Mary" };
List<Runnable> runners = new ArrayList<>();
int[] ind = {0};
for (int i = 0; i < names.length; i++){
ind[0] = i;
runners.add(() -> System.out.println(names[ind[0]]));
}
for (int k=0; k<runners.size(); k++){
runners.get(k).run();
}
}
}
On the contrary, if I use an enhanced for loop (while adding the Runnables), the correct (i.e. all the different) values are captured.
for (String name : names){
runners.add(() -> System.out.println(name));
}
Finally, if I use a classic for loop (while adding the Runnables), then I get a compilation error (which makes perfect sense, as the variable i
is not final or effectively final).
for (int i = 0; i < names.length; i++){
runners.add(() -> System.out.println(names[i]));
}
EDIT:
My point is: why is not the value of names[ind[0]]
captured (the value it has at the moment I add the Runnables)? It should not matter when I execute the lambda expressions, right? I mean, OK, in the version with the enhanced for loop, I also execute the Runnables later but the correct/distinct values were captured earlier (when adding the Runnables).
In other words, why does not Java always have this by value / snapshot semantics (if I may put it this way) when capturing values? Wouldn't it be cleaner and make more sense?