Я думаю, что встретил эту классическую ситуацию в JavaScript.
Обычно программист ожидает, что этот код ниже будет напечатан "Питер", "Пол", "Мэри".
Но это не так. Может ли кто-нибудь объяснить, почему это работает на Java?
Этот код Java 8 компилируется и печатает 3 раза "Мэри".
Я предполагаю, что это вопрос того, как он реализован в глубине души но... разве это не означает неправильную реализацию?
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();
}
}
}
Напротив, если я использую расширенный цикл for (при добавлении Runnables), будут зафиксированы правильные (т.е. все разные) значения.
for (String name : names){
runners.add(() -> System.out.println(name));
}
Наконец, если я использую цикл classic для цикла (при добавлении Runnables), тогда я получаю ошибку компиляции (что имеет смысл, поскольку переменная i
не является окончательной или фактически окончательной).
for (int i = 0; i < names.length; i++){
runners.add(() -> System.out.println(names[i]));
}
EDIT:
Моя точка зрения: почему не значение names[ind[0]]
уловлено (значение, которое оно имеет в момент добавления Runnables)? Не важно, когда я выполняю лямбда-выражения, не так ли? Я имею в виду, ОК, в версии с расширенным циклом, я также запускаю Runnables позже, но правильные/различные значения были записаны ранее (при добавлении Runnables).
Другими словами, почему Java не всегда имеет эту по значению/моментальной копии семантику (если можно так выразиться) при захвате значений? Разве это не было бы чище и иметь больше смысла?