skip to content

What is definite assignment in Java, and why does the compiler reject reading a local variable that might be unassigned?

level: juniorimportance: must knowfreq 62%

answer

  1. Locals have no default; fields do
  2. DA = assigned on EVERY path before a read
  3. Compile-time, structural, conservative
  4. Constant expressions (true/false) are special-cased
  5. Only reads are constrained, not assignments

basics

~20 s

Definite assignment is a compile-time check that every local variable is given a value before you read it. If the compiler can find any path where the variable might not be set yet, it refuses to compile and reports a "variable might not have been initialized" error.

solid answer

~40 s

Definite assignment is a static analysis the Java compiler runs to guarantee that every local variable has a definitely-assigned value at the point it is read. Unlike fields, locals get no default value, so reading an unset local would expose garbage; the language forbids it at compile time instead. The compiler tracks, for each program point, whether a variable is "definitely assigned" along every possible execution path leading there. If any path could reach a read without an assignment, you get a compile error. This makes a whole class of uninitialized-variable bugs impossible. The analysis is conservative and path-based: it works on the structure of the code (if/else, loops, try/catch, switch), not on actual runtime values, so it can occasionally reject code that would in practice always assign.

go deeper

for a junior

Knows locals must be initialized before use and that the compiler enforces it, recognizes the "might not have been initialized" error.

for a middle

Can explain the every-path requirement, distinguishes locals from fields, and knows the check is compile-time and conservative.

for a senior

Articulates that the analysis is structural/path-based per JLS, special-cases constant expressions, and applies to blank finals; can predict when the compiler will reject seemingly-fine code.

for a principal

Frames it as a safety invariant of the type/flow system, contrasts with languages lacking it, and can discuss the trade-off between conservatism and false rejections, plus its interaction with final and exceptions.

## What problem this solves In Java a **local variable** (a variable declared inside a method, constructor, or block) does **not** receive a default value. Compare this with **fields** (variables declared at class level), which are automatically initialized to `0`, `false`, `null`, etc. If locals also silently defaulted, you could accidentally read a variable you forgot to set and get meaningless data. To prevent that entire bug category, the Java Language Specification (JLS, the official rulebook for the language) requires that a local variable be **definitely assigned** before any access that reads its value. ## What "definitely assigned" means At every point in the program, the compiler classifies a variable as either *definitely assigned* (DA) or *not*. A variable is DA at a given point if **every possible execution path** that reaches that point has already executed an assignment to it. "Every path" is the key phrase: it is not enough for *some* branch to assign the variable — *all* branches that can reach the read must. ```java int x; if (cond) { x = 1; } System.out.println(x); // ERROR: if cond is false, x is unassigned here ``` The `else` path (cond false) reaches the `println` without assigning `x`, so `x` is **not** definitely assigned there, and the compiler rejects it. ```java int x; if (cond) { x = 1; } else { x = 2; } System.out.println(x); // OK: every path assigns x ``` Now both branches assign `x`, so after the `if/else` it is definitely assigned. ## It is static and conservative The analysis runs at **compile time** and looks only at the *shape* of the code — the control-flow structure — never at the runtime values of variables. So it is **conservative**: it may reject code that, by human reasoning, would always assign the variable. ```java int x; if (true) { // a true constant: the compiler DOES special-case this x = 1; } System.out.println(x); // OK only because `true` is a constant expression ``` The compiler does evaluate **constant expressions** (`true`, `false`, `1 == 1`) when deciding reachability, but it will not reason about an ordinary boolean variable whose value it cannot know statically. ## Reading vs. assigning The rule only constrains a variable's **use as a value (a read)**. Assigning a value is always fine; declaring is fine. So: ```java int x; // declaration only — fine x = compute(); // assignment — fine int y = x; // read of x — requires x be definitely assigned (it is) ``` ## Where it applies Definite assignment governs **local variables** and **blank `final` fields/parameters** (finals with no initializer). It does **not** apply to ordinary non-final fields, because those have language-defined defaults. The rules cover all control structures — `if`, `while`, `for`, `do`, `switch`, `try/catch/finally`, the conditional operator `?:`, `&&`/`||`/`!`, `break`/`continue`/`return`/`throw` — each with its own JLS rule for how DA flows through it. ## Why it matters This check turns a common runtime mistake (using an uninitialized value) into an immediate compile-time error, with zero runtime cost. It is one of the reasons Java programs rarely exhibit the "reading garbage memory" bugs familiar from languages where locals are not checked.

  • Do fields need definite assignment too?
    Ordinary (non-final) fields don't — they get default values (0/false/null). Blank final fields and blank final locals/parameters DO require definite assignment before use, and final fields additionally require definite *unassignment* before assignment.
  • Why doesn't the compiler just default locals to 0/null like fields?
    It's a deliberate design choice: forcing explicit initialization catches "forgot to set it" bugs at compile time. A silent default would hide those bugs and make code intent ambiguous.

It's like a checklist at airport security: every passenger (every code path) must show a boarding pass (an assignment) before they reach the gate (the read). It is not enough that most passengers have one.

saying these in an interview costs you the question

  • Saying locals default to 0/null like fields — they don't, that's the whole point
  • Claiming the check inspects runtime values — it is purely static/structural
  • Thinking an assignment in just one if-branch is enough
  • Confusing definite assignment (must be set before read) with definite unassignment (final must NOT be set yet)

context