Mastery
Mastery/Java/A. Object model, identity & memory
T2 · intermediate depth

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

java
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 + ")");
java
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

Interview angle

A precise question that catches a common misconception: "if a field is final, is the object it points to immutable?" Many candidates conflate the two. The correct answer — final only locks the reference, not the referenced object's state — is exactly the distinction that matters for writing genuinely immutable classes, not just ones that look immutable at a glance.

In the industry

Real immutability requires every field to be final and to only ever point at immutable objects (or defensively copy mutable ones) — a final List<String> field is not actually immutable unless you also never expose a mutable reference to that list. "Effectively final" is what makes local-variable capture in lambdas work without ceremony; understanding it explains why some perfectly reasonable-looking code doesn't compile the moment a captured variable gets reassigned anywhere in its scope.