skip to content

What is a 'blank final' field and what must every constructor do for it?

level: middleimportance: must knowfreq 62%

answer

  1. Declared with no value = blank final
  2. Every constructor assigns it once on every path
  3. Delegate via this(...) to avoid double assignment
  4. Can't combine declaration init + constructor assignment

basics

~20 s

A blank final is a final field declared without a value, like private final int id;. Because it has no initializer, every constructor must assign it exactly once before the constructor ends. If any constructor leaves it unset, the code won't compile.

solid answer

~50 s

A blank final is a final field that is declared without an initializer, so its value is decided later — at construction time. The rule is that on every code path through every constructor, the blank final must be definitely assigned exactly once before the object is fully built. This lets different constructors choose different values (e.g. a value passed as an argument) while still preserving the once-and-only-once guarantee. If a class has multiple constructors, each one must assign the blank final; alternatively a constructor can delegate via this(...) to another constructor that does the assignment, so it isn't assigned twice. You cannot assign it both at the declaration and in a constructor — that would be two assignments. Blank finals are the standard way to make immutable objects whose values come from constructor parameters.

go deeper

for a junior

Recognizes a blank final as a final field with no initializer that gets a value in the constructor.

for a middle

States that every constructor must definitely assign the blank final exactly once and can spot the missing-else and double-assignment errors.

for a senior

Uses constructor delegation (this(...)) correctly and reasons about definite assignment across all constructor paths.

for a principal

Designs immutable value types around blank finals and constructor invariants, balancing builders vs. telescoping constructors for assignment correctness.

## Definition A **blank final** is a `final` field that is **declared without being initialized** at the point of declaration: ```java private final int id; // blank final — no value yet ``` Contrast with a non-blank final, which is given its value where it is declared: `private final int id = 5;`. The word *blank* means 'left empty for now'. The value is supplied **later**, during object construction. ## The obligation it creates Because a `final` field must be **definitely assigned exactly once**, and a blank final has *not* been assigned at declaration, the responsibility shifts to **construction**. Concretely: - **Every constructor** must definitely assign the blank final, on **every** code path (every branch of every if/switch, etc.) before the constructor completes. - It must be assigned **exactly once** — assigning it twice (e.g. once at the top and again in an `if` branch where it was already set) is a compile error. If a class declares three constructors and only two of them assign the blank final, the third fails to compile. ## Why blank finals are useful They let the value be **chosen per-object at runtime** while keeping immutability: ```java class User { private final String name; // blank final User(String name) { this.name = name; // value comes from the argument } } ``` You could not do this with a declaration initializer because the value isn't known until a `User` is created. ## Multiple constructors and constructor chaining When there are several constructors, each must assign the blank final. To avoid duplicating logic, a constructor can **delegate** to another using `this(...)`: ```java class User { private final String name; User(String name) { this.name = name; } // assigns the blank final User() { this("anonymous"); } // delegates; does NOT assign again } ``` The no-arg constructor must **not** assign `name` itself, because the constructor it calls already does — assigning again would be a second assignment and a compile error. ## Conditional assignment is allowed if every path assigns once ```java final int sign; if (value >= 0) { sign = 1; } else { sign = -1; } // OK: every path assigns sign exactly once ``` This compiles because both branches assign `sign` exactly once and the compiler can prove it. But: ```java final int sign; if (value >= 0) sign = 1; // ERROR: else path leaves sign unassigned ``` fails, because the missing `else` means a path could finish with `sign` unset. ## Common pitfall: declaration + constructor ```java private final int id = 0; User(int id) { this.id = id; } // ERROR: id was already assigned at declaration ``` This is two assignments. To set `id` from the constructor, leave it blank at declaration. ## Summary A blank final defers a `final` field's value to construction. Every constructor (directly or by delegating with `this(...)`) must assign it exactly once on every path. This is the idiomatic mechanism for immutable objects parameterized by constructor arguments.

  • If two constructors both need the same blank final value, how do you avoid assigning it twice?
    Use constructor delegation: one constructor assigns the field, and the others call it via this(...). The delegating constructor must not assign the field itself.
  • Can a blank final be assigned inside an if-branch without an else?
    Only if every path still assigns it exactly once. An if with no else that assigns the field only in the if-branch leaves the field possibly unassigned, which is a compile error.

saying these in an interview costs you the question

  • Assigning a blank final at declaration AND in a constructor (double assignment)
  • Forgetting one constructor in a multi-constructor class
  • An if without an else where only the if-branch assigns the blank final

context