Polymorphism pitfalls & constructors
Constructor call order under inheritance, the calling-an-overridable-method-in-a-constructor trap, and what is NOT polymorphic.
The order of construction under inheritance
When you create a subclass object, Java builds it base-first. The exact order is:
- The superclass constructor runs (recursively, up to
Objectfirst). - The subclass's instance field initializers run, in source order.
- The rest of the subclass constructor body runs.
This base-first rule sets up the famous trap.
Worked example: a constructor that calls an overridable method
class Glyph {
Glyph() { draw(); } // calls an overridable method!
void draw() { System.out.println("Glyph.draw"); }
}
class RoundGlyph extends Glyph {
private int radius = 5; // field initializer
@Override void draw() {
System.out.println("RoundGlyph.draw, radius = " + radius);
}
}
Now new RoundGlyph(); prints:
RoundGlyph.draw, radius = 0
Why 0, not 5? Trace it:
RoundGlyph's construction begins; the implicitsuper()runsGlyph()*first*.- Inside
Glyph(),draw()is called. Becausedraw()is overridden and dispatch is *dynamic*, it runsRoundGlyph.draw()— the subclass version — even though we are still inside the superclass constructor. - But
RoundGlyph's field initializer (radius = 5) has not run yet — it runs *after*super()returns. Soradiusstill holds its default value,0.
The overridden method runs against a half-built object. The lesson: never call an overridable (non-final, non-private, non-static) method from a constructor. Either make such helper methods final/private, or don't call them during construction.
What is NOT polymorphic
Late binding applies *only* to overridable instance methods. Three things resolve by the static (declared) type instead, and the exam loves to test this:
- Fields are not polymorphic. A field reference is resolved by the declared type. If both Super and Sub declare a field x, then Super ref = new Sub(); ref.x reads Super's x (the field is *hidden*, not overridden).
- static methods are not polymorphic. They are hidden, not overridden, and bound to the declared type.
- private methods are not overridable at all. A subclass method with the same name is a *new, independent* method; the superclass's own code keeps calling the superclass's private version.