Why is it dangerous to call an overridable (non-final, non-private) method from inside a constructor? What can go wrong?
answer
- Parent ctor runs while child fields are still default (0/null)
- Dynamic dispatch is live during construction → override runs early
- Symptom: NPE / zero from a half-built object
- Call only private/static/final from a constructor
- Same hazard in clone() and readObject()
basics
~20 sIf a parent constructor calls a method the child overrides, the child's version runs while the child object isn't finished being built yet. The child's fields are still at their defaults (0/null), so the override can misbehave or crash.
solid answer
~50 sJava uses dynamic dispatch: an overridden method call always resolves to the most-derived implementation, even when the call originates in a superclass constructor. The problem is timing. Constructors run parent-first, so when the parent constructor runs, the child's own field initializers and constructor body have *not* run yet — the child's fields still hold their default values (0, false, null). If the parent constructor calls an overridable method that the child overrides, the child's override executes against this half-built object and sees those uninitialized fields. The classic symptom is a NullPointerException or wrong/zero values from an override that assumed its fields were set. The fix is to never call overridable methods from a constructor; call only private, static, or final methods (which can't be overridden), or move the logic out of construction (e.g. a factory or init step run after the object is fully built).
code
java · 18 linesclass Base {
Base() {
// Dynamic dispatch: this calls Derived.greet() during construction
System.out.println(greet());
}
String greet() { return "hello from Base"; }
}
class Derived extends Base {
private final String name = "world"; // initializer runs AFTER super()
@Override
String greet() {
// 'name' is still null here when called from Base's constructor
return "hello " + name.toUpperCase(); // NullPointerException!
}
}
// new Derived(); -> throws NPE: Base() -> Derived.greet() -> name is nullgo deeper
Recognizes that calling a method from a constructor can be risky and that fields may not be set yet; may not yet explain dispatch.
Explains that dynamic dispatch picks the subclass override and that the subclass fields are still at defaults during super-construction, producing NPEs.
Articulates the precise interaction of parent-first construction and live dynamic dispatch, gives the safe alternatives (private/static/final, post-construction init), and avoids the 'final fixes it' trap.
Frames it as the Effective Java inheritance-design rule, extends the hazard to clone()/readObject(), contrasts Java's uniform dispatch with C++/C#'s constructor-type resolution, and reasons about API design to make classes safe to subclass or final by default.
## Terms - *Overriding* is when a subclass provides its own implementation of a method declared in a superclass, with the same signature. - *Dynamic dispatch* (a.k.a. virtual call, late binding) is Java's rule that a method call on an object invokes the implementation belonging to the object's *actual runtime type*, not the type of the reference or the class where the call textually appears. - A method is *overridable* if it is instance-level and not `private`, `static`, or `final` — those three can't be overridden, so calls to them are resolved without dynamic dispatch to a subclass. ## The two facts that collide - (1) Construction runs ***parent-first***: a superclass constructor body runs before the subclass's field initializers and constructor body. - (2) Dynamic dispatch is ***already active*** during construction — `this` already has its final runtime type. So if a superclass constructor calls an overridable method, and a subclass overrides it, the *subclass* version runs — but at that moment the subclass hasn't initialized its own fields. They still hold Java's default values: `0` for numeric primitives, `false` for `boolean`, `null` for references. ## Concrete failure Suppose `Base`'s constructor calls `render()`, and `Derived` overrides `render()` to use a field `Derived.color` that `Derived`'s constructor sets to `"red"`. When you do `new Derived()`: 1. Base's constructor runs first 2. it calls `render()` 3. dynamic dispatch picks `Derived.render()` 4. but `color` is still `null` because Derived's constructor body hasn't run yet 5. NullPointerException (or it silently uses the wrong value). Even more subtly, if Derived sets the field both via initializer (`color = "red"`) and the override reads it during super-construction, the override sees `null`; *then* the initializer runs and sets `"red"`, so the field ends up correct but the override already misbehaved with the wrong value. ## Why Java doesn't 'fix' it Java deliberately keeps dynamic dispatch uniform; it does not switch to the static-type method just because you're inside a constructor (some other languages, like C++/C#, resolve to the constructor's own class type instead — a different trade-off). So in Java **the burden is on the programmer**. ## Safe alternatives - (a) Make the called method `private`, `static`, or `final` so no subclass can override it — the call is then guaranteed to run the intended code. - (b) Don't do real work in the constructor; expose an `init()`/`start()` method the caller invokes after construction, or use a **static factory method** that constructs then initializes. - (c) Make the class `final` if it's not meant to be subclassed, eliminating overrides entirely. - (d) For required setup that depends on subclass state, pass that state up through `super(...)` arguments so the parent has it during construction. ## Effective Java guidance This is Item "Design and document for inheritance or else prohibit it": **constructors must not invoke overridable methods, directly or indirectly.** The same hazard applies to `clone()` and `readObject()` (deserialization), which also behave like constructors in this respect.
- Does making the field final prevent this bug?No. A final field can be assigned during construction; during the superclass constructor it still holds its default value because the subclass hasn't assigned it yet. Finality controls reassignment, not initialization timing.
- Which methods are safe to call from a constructor?Private, static, and final methods — none can be overridden by a subclass, so the call always runs the intended implementation rather than a subclass override against a half-built object.
- Do clone() and deserialization have the same problem?Yes. Both create objects without running normal constructors but still invoke overridable methods on a not-yet-fully-initialized instance, so calling overridable methods from them is equally unsafe.
saying these in an interview costs you the question
- Claiming the superclass version runs (it doesn't — the override does, via dynamic dispatch)
- Saying 'just initialize the field before super()' — you can't run subclass field init before super()
- Thinking final on the field fixes it (the field is still unset during super-construction)
- Believing this only affects fields set in the constructor body — field *initializers* are also still pending