Card 07/ 09

GotchaDifficulty: Advanced1 min

Captured Variables: Why a Lambda Cannot Change a Local

A running total inside forEach, and a compile error about final when nothing says final.

A lambda may read a local variable of the method around it, but not assign to it, and the variable may not be reassigned anywhere else either. The compiler calls such a variable effectively final: never assigned after its first value.

A total incremented inside a forEach shows the error:

java
int total = 0;
invoices.forEach(invoice -> total += invoice.pence());
text
Report.java:2: error: local variables referenced from a lambda expression must be final or effectively final

A lambda captures a copy of the local variables it uses, because the method that declared them may have returned long before the lambda runs. Allowing the lambda to assign to its copy would look like changing the original and would not be.

So the compiler insists that captured locals never change at all: declared final, or never assigned to after their first value.

The real answer is that summing in a forEach is the wrong shape. Let the pipeline produce the value rather than accumulating into something outside it.

java
long total = invoices.stream().mapToLong(Invoice::pence).sum();