Lambda capture / closure
A lambda capture (or closure) is a local variable from the enclosing method that a lambda uses. Java lambdas can capture only effectively final variables β those never reassigned after initialisation.
Effectively final
int multiplier = 10; // effectively final
Function<Integer, Integer> times = n -> n * multiplier; // OK
int count = 0;
list.forEach(x -> count++); // COMPILE ERROR β count is not effectively final
Workaround for mutation
If you need the lambda to "mutate" something, use a mutable container:
int[] count = { 0 };
list.forEach(x -> count[0]++); // OK β array reference is final, contents are not
AtomicInteger counter = new AtomicInteger(0);
list.forEach(x -> counter.incrementAndGet());
Better yet β use stream reductions designed for this: list.stream().count(), .reduce, .collect.
Why the restriction exists
Java lambdas are not full closures in the functional-programming sense. Capturing a mutable variable reference would require synchronised access (because lambdas can be executed on other threads via parallel streams, executors, or callbacks). The "effectively final" rule sidesteps this entirely.
Capturing instance fields
Lambdas inside instance methods implicitly capture this, which gives access to all instance fields β mutable or not. Be aware that this creates a hidden strong reference from the lambda to the enclosing instance, which can cause memory leaks if the lambda outlives the instance (event listeners, callbacks).