Компилятор преобразовывает лямбды в анонимные классы. Эти классы могут менять поля объемлющего объекта или других объектов, на которые имеют ссылки. Но передаваемые в лямбды локальные переменные должны быть неизменяемыми, так как замыкания в Java
эмулируются созданием в анонимном классе поля, захватывающего значение замыкаемой переменной. Убедиться в этом просто. Скомпилируем класс
public class Main {
public static void main(String[] args) throws Exception {
String value = "Test";
Supplier<String> lambda = () -> value;
}
}
и запустим с параметром
-Djdk.internal.lambda.dumpProxyClasses=.
, чтобы Java сохранила сгенерированный анонимный класс. Заглянем внутрь этого класса
$ javap -p Main$$Lambda$1
final class Main$$Lambda$1 implements java.util.function.Supplier {
private final java.lang.String arg$1; // Захваченная переменная value
private Main$$Lambda$1(java.lang.String);
private static java.util.function.Supplier get$Lambda(java.lang.String);
public java.lang.Object get();
}
Об этом можно почитать у Брайана Гетца в "
State of the Lambda: Variable capture".