final vs. true immutability, "effectively final" for lambda capture
final on a variable only prevents reassigning the reference -- it says nothing about whether the object
it points to can be mutated. A final MutablePoint p still lets you freely change p.x; final just stops
you from doing p = somethingElse. Separately, a local variable captured by a lambda doesn't need an
explicit final keyword -- it just needs to be effectively final (never reassigned after
initialization, whether or not you wrote the keyword).
class MutablePoint {
int x, y;
MutablePoint(int x, int y) { this.x = x; this.y = y; }
}
final MutablePoint p = new MutablePoint(1, 2);
p.x = 99; // perfectly legal -- final only locks the REFERENCE, not the object's contents
System.out.println("final reference, freely mutated contents: (" + p.x + "," + p.y + ")");
import java.util.function.Supplier;
int localVar = 10;
Supplier<Integer> s = () -> localVar * 2; // no `final` keyword, but never reassigned -> effectively final
System.out.println("captured effectively-final local: " + s.get());
Reassigning a captured local after the fact is a real compile error, not just a style warning --
reproduced verbatim from a real javac run:
NotEffectivelyFinal.java:5: error: local variables referenced from a lambda expression must be final or effectively final
Supplier<Integer> s = () -> localVar * 2;
^
1 error