All 15 Functional Interfaces & Method References questions
More in Functional Interfaces & Method References
Advertisement
Back to results for “Concurrency”
Java › Functional Interfaces & Method References

How does a lambda expression capture variables from its enclosing scope, and why must captured local variables be effectively final?

Advanced

Answer

A lambda capturing a local variable from its enclosing method doesn't actually capture the variable itself, but rather captures a copy of its value at the time the lambda is created (for local variables; instance/static fields are captured by reference to the containing object, allowing genuine mutation) -- Java enforces that any captured local variable must be effectively final (never reassigned after initialization, even if not explicitly marked final) specifically because the lambda might execute later, on a different thread, after the enclosing method has already returned and its local stack frame no longer exists, so allowing the lambda to observe or modify a since-changed local variable would create ill-defined, thread-unsafe semantics.
public Supplier<Integer> makeCounter() {
    int count = 0;
    // count++; // if uncommented, would cause a COMPILE ERROR: count is no longer effectively final
    return () -> count;  // captures the VALUE of count (0) at lambda creation time
}

// Workaround for genuine mutable state: use an array or an AtomicInteger as a container
AtomicInteger counter = new AtomicInteger(0);
Runnable increment = counter::incrementAndGet;  // captures the reference, container itself is mutable
Real-world example A background task submitted with a lambda capturing a local loop variable in a for-loop is a classic pitfall (fixed in Java 8+ since each iteration effectively creates a fresh variable binding for enhanced for-loops, unlike some older languages), but attempting to have multiple lambdas share and mutate a captured counter directly requires wrapping it in a mutable container like AtomicInteger, since the raw local variable itself cannot be reassigned once captured.

Common follow-ups: Why does capturing an instance field (via 'this') not have the same effectively-final restriction that local variables do?;What's the specific mechanism (variable capture into a synthetic final copy) the compiler uses under the hood to implement this?