Careeroza
Effectively Final and Capturing Variables
The Rule
A lambda can read local variables from the surrounding method only if they are final or effectively final (never changed after assignment). The lambda captures the value, and Java forbids changes so that the copy and the original can never disagree, even when the lambda runs later on another thread.
int count = 0;
Runnable r = () -> System.out.println(count); // OK: count is effectively final
int total = 0;
list.forEach(x -> total += x); // COMPILE ERROR: local variables referenced from
// a lambda expression must be final or effectively finalWhat You Can Change
Example
Fields and the contents of objects can change. For counting, prefer a stream (sum()) or an AtomicInteger over the array trick.
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
static int fieldCounter = 0; // fields are not captured, so they can change
public static void main(String[] args) {
List<Integer> nums = List.of(10, 20, 30);
nums.forEach(n -> fieldCounter += n);
System.out.println("field: " + fieldCounter);
AtomicInteger atomic = new AtomicInteger();
nums.forEach(atomic::addAndGet);
System.out.println("atomic: " + atomic.get());
int best = nums.stream().mapToInt(Integer::intValue).sum();
System.out.println("stream sum (best): " + best);
String prefix = "Item-"; // effectively final
nums.forEach(n -> System.out.print(prefix + n + " "));
System.out.println();
}
}Output
field: 60 atomic: 60 stream sum (best): 60 Item-10 Item-20 Item-30
this Inside a Lambda
Note: Inside a lambda, this means the enclosing object. Inside an anonymous class, this means the anonymous object itself. A lambda does not create a new scope for names, so you cannot declare a parameter with the same name as a local variable.