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).