Ответ
Лямбда-выражение может обращаться к локальным переменным, параметрам метода или переменным класса, но с одним строгим ограничением: локальные переменные должны быть effectively final.
Правило effectively final: Переменная считается effectively final, если она не изменяет своего значения после инициализации (даже если ключевое слово final не указано явно).
Пример:
public void example() {
int localVar = 10; // effectively final
final int explicitFinal = 20; // явно final
int notFinal = 30;
notFinal = 40; // Изменение значения -> НЕ effectively final
Runnable r = () -> {
System.out.println(localVar); // OK
System.out.println(explicitFinal); // OK
// System.out.println(notFinal); // ОШИБКА компиляции!
};
r.run();
}
Причина ограничения: Лямбда может выполняться в другом потоке и после завершения метода, в котором она была объявлена. Локальные переменные хранятся в стеке и уничтожаются вместе с фреймом метода. Чтобы обеспечить доступ, Java захватывает значение переменной на момент создания лямбды. Если бы переменная могла меняться, возникли бы проблемы с видимостью изменений между потоками (потребовалась бы синхронизация). Захват по значению делает поведение предсказуемым и безопасным.
Поля класса (нестатические) не имеют этого ограничения, так как лямбда захватывает ссылку на объект this.