What does it mean to assign to a final variable, and how does final interact with assigning references vs. mutating objects?
answer
- final = assign exactly once
- final freezes the binding, not the object
- final List can still .add(); cannot reassign
- compound assignment to final = compile error
- immutability needs List.copyOf/unmodifiableList, not final
basics
~20 sA final variable can be assigned only once. After that you can't reassign it. But final only locks the variable, not the object it points to — you can still change the object's contents through that reference.
solid answer
~50 sMarking a variable `final` means it may be assigned exactly once; any later assignment (including compound assignment like `x += 1`) is a compile error. For a `final` local or field of a reference type, the *reference* is fixed, but the *object* it points to can still be mutated: `final List<String> list = new ArrayList<>(); list.add("a");` is fine, while `list = new ArrayList<>();` is not. final fields can be assigned either at declaration or once in every constructor (blank finals), and the compiler verifies definite single assignment. Compound assignment to a final variable always fails, because `x += 1` is a reassignment. This distinction — final freezes the binding, not the referenced object — is a frequent interview point and the reason `final` on a collection field doesn't make the collection immutable; you need `List.copyOf` / `Collections.unmodifiableList` for that.
code
java · 7 linesfinal java.util.List<String> list = new java.util.ArrayList<>();
list.add("a"); // OK: mutating the object
// list = new java.util.ArrayList<>(); // ERROR: reassigning a final variable
final int x = 1;
// x += 1; // ERROR: compound assignment is a reassignment
System.out.println(list + " " + x); // [a] 1go deeper
Knows a final variable can't be reassigned after it's set.
Distinguishes rebinding from mutation and knows final List can still be modified but not reassigned.
Explains blank finals, definite-assignment checking, effectively-final capture, and why immutability needs explicit copies.
Discusses JMM final-field safe-publication guarantees, defensive copying in API contracts, and when final improves reasoning vs. false sense of immutability.
## The two things a reference variable holds For a reference type, a variable holds a **reference** — a handle pointing at an **object** on the heap. Two separate things can change: 1. **Rebinding the variable** — making it point at a *different* object (an assignment, `=`). 2. **Mutating the object** — changing the object's internal state via methods/fields (`list.add(...)`, `point.x = 5`). `final` controls **only #1**. ## What `final` means A `final` variable can be **assigned exactly once**. The compiler enforces *definite assignment*: it must be assigned before use, and may never be assigned again. - **final local:** assign once, anywhere before first use. - **final field with initializer:** `final int n = 5;`. - **blank final field:** declared without a value, then assigned **exactly once in every constructor path** (or an instance initializer). The compiler checks all paths. - **static final:** assigned at declaration or in a static initializer, once. Any *second* assignment is a **compile error**, including compound assignment: ``` final int x = 1; x += 1; // ERROR: cannot assign a value to final variable x ``` That's because `x += 1` is `x = x + 1` — a reassignment. ## final does NOT make the object immutable ``` final List<String> list = new ArrayList<>(); list.add("a"); // OK — mutating the object, not rebinding list = new ArrayList<>(); // ERROR — rebinding the final variable ``` The reference is frozen; the `ArrayList` it points to is fully mutable. To get an immutable collection you need a different tool: `List.copyOf(...)`, `List.of(...)`, or `Collections.unmodifiableList(...)`. `final` and immutability are orthogonal. ## final and primitives For a primitive, there's no object to mutate, so `final int x` simply means the value can't change after its single assignment. A `static final` primitive (or `String`) with a compile-time constant initializer becomes a **constant expression**, usable in `switch` labels and inlined by the compiler. ## Why this matters - **Effectively final** locals (assigned once even without the keyword) can be captured by lambdas and anonymous classes; reassigning them breaks capture. The single-assignment discipline of `final` is what makes capture safe. - **Thread safety:** a properly constructed `final` field has special JMM (Java Memory Model) guarantees — its value is visible to other threads after construction without extra synchronization, *but only the reference*, not the mutable object it points to. - **API design:** marking fields `final` documents intent and prevents accidental reassignment, but you must still defensively copy or wrap mutable objects to achieve real immutability. ## Quick test for interviews Given `final` on a reference: ask "am I rebinding (illegal) or mutating (legal)?" Rebinding = new `=`; mutating = a method/field write on the pointed-to object.
- Does `final` on a field make the referenced object thread-safe?Only partially. The JMM guarantees the final field's value (the reference) is safely published after construction. But if the referenced object is mutable, concurrent mutation of that object still needs synchronization. final guarantees safe publication of the reference, not of the object's evolving state.
- Why can a lambda use a local variable without it being declared final?Because it must be effectively final — assigned once and never reassigned, even without the keyword. The capture copies the value at capture time, so reassignment would make the captured copy ambiguous; the compiler forbids it.
saying these in an interview costs you the question
- Thinking `final` makes the referenced object immutable
- Believing you can reassign a final variable in a second constructor branch
- Assuming compound assignment is exempt from the final restriction
- Conflating final with thread-safety of mutable state