Why is calling an overridable method from a constructor dangerous, and how does it relate to overriding rules?
answer
- Super constructor runs before subclass fields init
- Virtual dispatch reaches the override anyway
- Override sees default (null/0) fields
- Fix: call only final/private/static from constructors
- Effective Java: design for inheritance or forbid it
basics
~20 sIf a superclass constructor calls a method that a subclass overrides, the subclass's version runs before the subclass's own fields are initialized. So the override can see uninitialized (null/zero) fields and behave wrongly. Avoid calling overridable methods from constructors.
solid answer
~50 sConstruction runs top-down: the superclass constructor finishes before the subclass's field initializers and constructor body run. But method dispatch is always based on the runtime type, so if the superclass constructor calls an overridable instance method, the SUBCLASS override executes while the subclass is only half-built - its fields still hold defaults (null, 0, false). The override may then read those uninitialized fields and misbehave or NPE. This is a direct consequence of dynamic dispatch combined with construction order: overriding makes the call virtual, and virtual dispatch doesn't wait for the subclass to finish initializing. The fix is to never call overridable (non-final, non-private, non-static) methods from a constructor; instead call only final, private, or static methods, which cannot be overridden and therefore behave deterministically. Effective Java codifies this as 'design and document for inheritance or prohibit it', and constructors are a prime hazard.
code
java · 23 linesclass Base {
Base() {
init(); // BAD: overridable method called during construction
}
void init() { /* default */ }
}
class Derived extends Base {
private final String name = "set";
@Override
void init() {
// name is still null here: Base() ran before Derived's field initializer
System.out.println(name); // prints: null
}
}
new Derived(); // -> null
// Safe version: make the helper final or private so it cannot be overridden
class SafeBase {
SafeBase() { setup(); }
private void setup() { /* deterministic, never dispatches to a subclass */ }
}go deeper
May not know this trap; at most recognizes that constructors run parent-first.
Can describe that a constructor calling an overridden method can see uninitialized subclass state and that it should be avoided.
Explains the interplay of top-down construction order with virtual dispatch and prescribes final/private helpers as the fix.
Articulates it as a consequence of the overriding/dispatch model, ties it to Effective Java's 'design for inheritance or forbid it', extends it to clone()/readObject() and final-field initializers, and advocates final classes / factory-with-post-init patterns to eliminate the class of bug.
## The two facts that collide **Fact 1 - construction order (top-down).** When you create a subclass instance, initialization proceeds from the top of the hierarchy down: 1. The subclass constructor implicitly (or explicitly) calls the superclass constructor *first* (`super(...)`). 2. The superclass's field initializers and constructor body run **to completion**. 3. Only *then* do the subclass's own field initializers run, followed by the rest of the subclass constructor body. So at the moment the **superclass** constructor is executing, the **subclass's** fields have not yet been assigned their intended values - they still hold their **default values** (`null` for references, `0`/`0.0` for numbers, `false` for booleans). **Fact 2 - dynamic dispatch ignores construction state.** A call to an overridable instance method is **virtual**: it dispatches on the object's **runtime type**. During construction of a subclass instance, the runtime type is already the subclass. So even a call made from *within the superclass constructor* dispatches to the **subclass's override**. ## The collision Put them together: the superclass constructor calls an overridable method → the subclass override runs → but the subclass's fields are still at their defaults because step 3 hasn't happened yet. The override sees a half-constructed object. ```java class Base { Base() { init(); } // calls overridable method during construction void init() { } } class Derived extends Base { private final String name = "set"; @Override void init() { System.out.println(name); // prints null! field not yet initialized } } new Derived(); // output: null ``` Even though `name` has an initializer, it prints **null**, because `Base()` runs (and calls the overridden `init()`) *before* `Derived`'s field initializer assigns `"set"`. If `init()` did `name.length()`, you'd get a `NullPointerException` in surprising-looking code. ## Why this is fundamentally about overriding rules The danger exists *because* `init()` is **overridable**. Overriding makes the dispatch virtual; virtual dispatch is what reaches into the not-yet-initialized subclass. If the method could **not** be overridden, the superclass constructor would call its *own* version deterministically and no half-built subclass code would run. That is the escape hatch: - A **`final`** method cannot be overridden. - A **`private`** method is not inherited/overridable (it is invisible to subclasses). - A **`static`** method is not subject to instance overriding. Calling only such non-overridable methods from a constructor removes the hazard. This is exactly the inverse of the overriding rules: the same property (overridability → virtual dispatch) that powers polymorphism becomes a liability during the narrow window where the object is partially built. ## The guidance Effective Java (Item: 'Design and document for inheritance or else prohibit it') states the rule directly: **constructors must not invoke overridable methods.** Practical mitigations: 1. Don't call overridable methods from constructors (or from `clone()`/`readObject()`, which act like constructors). 2. If a constructor needs to run shared setup, make that helper `private` or `final`. 3. If a class is not designed for safe extension, make it `final` to forbid subclassing entirely. 4. Prefer factory methods / builders that fully construct and then invoke any 'post-init' hook *after* the object is complete. ## Related subtleties - The same trap applies to `final` *instance fields with initializers*: they are not yet assigned when a superclass constructor runs, so even reading a `final` field from an overridden method during construction can see its default. - Static and instance initializer blocks follow the same top-down ordering and have the same exposure. - This is one reason immutable, `final` classes (no subclassing) sidestep an entire category of construction bugs.
- How do you make a constructor's helper method safe to call?Make it private, final, or static so it cannot be overridden. Then the call is not virtual with respect to subclasses, so the superclass constructor always runs its own deterministic version and never reaches a half-initialized subclass.
- Does declaring the subclass field `final` with an initializer prevent the null-read?No. Even a final field with an initializer is still at its default value while the superclass constructor (and thus the override it calls) runs, because subclass field initializers execute after super() returns. The override can still observe null/0.
It's like a building's ground-floor crew (superclass) phoning the penthouse's procedure (the override) before the penthouse has been furnished. The procedure runs, but every cupboard it reaches for is still empty - the movers haven't been up there yet.
saying these in an interview costs you the question
- Assuming subclass fields are initialized before the superclass constructor finishes - it is the reverse.
- Thinking dynamic dispatch is suspended during construction - it is not; the override still runs.
- Believing a final field's initializer protects against the early-read - it does not.
- Suggesting the fix is to make the field volatile or synchronized - the issue is ordering, not visibility.