You're designing a base class meant to be extended by others. What construction-order rules and design choices keep subclassing safe, and what should you do if you can't guarantee safety?
answer
- Constructor calls only private/static/final methods
- Take needed state via super(...) params, not overridable getters
- Document self-use + intended override hooks (@implSpec)
- Defer real work to post-construction init/factory
- Can't make it safe → make it final or use factories + composition
basics
~20 sDon't call overridable methods from the base constructor; only call private/final/static ones. Document exactly what subclasses may do. Require needed state via super(...) parameters. If you can't make it safe, make the class final so it can't be subclassed.
solid answer
~50 sBecause construction runs parent-first while dynamic dispatch is live, a base constructor that calls an overridable method runs subclass code against an unfinished object. So the first rule: a constructor (and clone()/readObject()) must never invoke overridable methods — restrict calls to private, static, or final methods. Second, demand the state you need up front: make the base constructor take the parameters it requires so it never has to ask a subclass mid-construction. Third, document for inheritance — specify which methods are designed to be overridden, their self-use (which methods call which), and any ordering guarantees, so subclassers know the contract. Provide protected hooks deliberately rather than leaking internals. Keep construction side-effect-free where possible, deferring real work to a post-construction init/start step. Finally, if you cannot guarantee these properties, prohibit inheritance: make the class final, or give it only package-private/private constructors plus static factories. 'Design and document for inheritance, or else prohibit it.'
go deeper
May know to avoid doing complex work in constructors but not the inheritance-design rationale.
States the 'no overridable methods in constructors' rule and suggests making classes final when not designed for extension.
Lays out the full rule set (no overridable calls, state via params, documented self-use, deferred init) and applies it to clone()/readObject().
Frames inheritance as an explicit API-design contract — documents self-use, minimizes the protected surface, weighs composition vs inheritance, and decides deliberately between designing-for-extension and prohibiting it, considering evolvability and the fragile-base-class problem.
## The underlying hazard (recap) Java constructs objects **parent-first**: a superclass constructor body runs before the subclass's field initializers and constructor body. Method dispatch is *virtual* throughout — `this` already has its final runtime type — so any overridable method called from the base constructor resolves to the subclass override, which then runs against an object whose subclass fields are still default (0/false/null). This is the root cause that all the design rules below defend against. ## The design rules 1. **Rule 1 — Constructors call no overridable methods.** Directly or transitively, a constructor must invoke only methods that cannot be overridden: `private`, `static`, or `final` instance methods. This guarantees the constructor always runs the code it intends, never a subclass's premature override. The same applies to `clone()` and `readObject()` (deserialization), which create objects without normal constructors but still dispatch virtually on a partially-built instance. 2. **Rule 2 — Take required state as constructor parameters.** If the base needs a value to initialize itself, make the base constructor accept it (`super(config)`), rather than calling an overridable `getConfig()` the subclass is expected to supply. This eliminates the need to consult subclass state during base construction. 3. **Rule 3 — Design and document the self-use pattern.** *Self-use* is when a class's methods call its own overridable methods. A subclasser must know this to override correctly. Document (e.g. with an '@implSpec'-style note) which methods are overridable hooks, what they must/must not do, and in what order the class invokes them. Without this, an override can break the base class's internal invariants. 4. **Rule 4 — Provide deliberate hooks, not leaks.** Expose `protected` methods only where extension is intended and safe. Minimize the protected surface; every protected member is a lifelong commitment to subclassers. Avoid exposing internal mutable state. 5. **Rule 5 — Keep construction inert; defer real work.** Prefer constructors that only assign validated fields. Side-effecting or polymorphic setup belongs in a post-construction `init()`/`start()` method, or behind a static factory that constructs and then initializes the fully-built object — sidestepping the half-built hazard entirely. (This also pairs well with the Builder pattern for many parameters.) 6. **Rule 6 — If you can't guarantee safety, prohibit inheritance.** Make the class `final`, or give it only non-public constructors and expose `static` factory methods. A class not explicitly designed and documented for inheritance should not be subclassable — accidental inheritance is a common source of fragile subclasses (the 'fragile base class' problem). **Composition/delegation** (a wrapper that forwards to a held instance) is the safer reuse mechanism when inheritance isn't designed for. ## Why this is a senior/principal concern These rules trade short-term convenience for long-term API safety and evolvability. Getting them wrong creates classes that 'work' until someone subclasses them, then fail mysteriously: - NPEs during construction; - broken invariants on override. They embody Effective Java's 'Design and document for inheritance or else prohibit it' and 'Favor composition over inheritance', and underpin why so many JDK and framework classes are `final` or rely on factories.
- If a framework's lifecycle genuinely needs a polymorphic callback during setup, how do you do it safely?Move the callback out of the constructor: fully construct the object first, then have a factory or the framework call an init/start lifecycle method on the completed instance. The object is fully initialized before any overridable method runs.
- How does 'favor composition over inheritance' relate to these rules?Composition (holding an instance and forwarding to it) avoids the construction-order and fragile-base-class hazards entirely, since there's no overriding of a half-built superclass. When inheritance isn't carefully designed and documented, a forwarding wrapper is the safer reuse mechanism.
saying these in an interview costs you the question
- Calling an overridable template method from the base constructor 'because it's the framework pattern'
- Exposing protected hooks without documenting their construction-time constraints
- Assuming subclasses will 'just know' not to rely on construction-time callbacks
- Treating inheritance as the default reuse mechanism instead of composition