Изменить локальную переменную, в ламбде, стандартными методами невозможно, потому, что локальная переменная должны быть effectively final, то есть не должна изменяться после объявления.
Это сделано потому что локальная переменная помещается в стек, поэтому её время жизни ограничено скоупом. Однако экземпляр лямбды, который захватил данную переменную, может передать её наружу и использовать в другом потоке или после окончания работы метода.
Для того, чтобы решить данную проблему в лямбда-выражение помещается копия требуемой локальной переменной, которая независима от оригинала. Но в таком случае копия и локальный оригинал будут разными, независящими друг от друга переменными, а это может привести к сложностям, из-за возможности использования неактуальных, устаревших значений. Чтобы значение было всегда актуально оно не должно меняться.
Чтобы изменить локальную переменную нужно отправить её в кучу. Чтобы это сделать, можно использовать обёртку, например, одноэлементный массив или класс с нужной переменной. Однако это не решает проблему с актуальностью. Поэтому, при использовании многопоточности, рекомендуется уделить внимание синхронизации работы потоков с данной переменной.
void doLater(Runnable r){
new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
}).start();
}
void main() {
int i = 0;
doLater(() -> {
// Ошибка - к этому моменту переменная может быть 0, 1, или перестать существовать.
i += 42;
});
i++; // Было бы здесь 43 или 1 - неизвестно.
}
void hackO {
int[] i = {0};
doLater(() -> {
// Теперь это компилируется, но поведение непредсказуемо – опасно
i[0] += 42;
});
i[0]++;
}