2.1.11 · D5OOP Fundamentals
Question bank — Method overriding — when and why
Before you start, hold these four anchors in mind:
- Signature = the method's name plus its parameter list (types + order). "Same signature" means both must match.
- Declared type = the type written on the variable (
Shape s). Runtime type = the actual object made (new Circle()). - Dynamic dispatch = the runtime picking the method body using the real object's class.
- Substitutability (Liskov Substitution Principle) = a child object must be safely usable everywhere the parent was promised.
True or false — justify
Answer true or false, then give the one-sentence reason. The reason is the real test.
Overriding is resolved at compile time.
False — overriding is resolved at runtime by the object's actual class; only overloading is resolved at compile time by argument types.
A method with the same name but different parameters in a child overrides the parent's method.
False — different parameters means it is a new overload, not an override; overriding requires the identical signature, so the parent method still exists untouched.
Shape s = new Circle(); s.area(); runs Shape.area.
False — the runtime object is a
Circle, so dynamic dispatch runs Circle.area; the declared Shape type only decided the call was allowed to compile.You can override a private method so the parent's callers use your version.
False — private methods are not visible to subclasses, so a same-named child method is a brand-new method, not an override; nothing dispatches to it from the parent.
An override may return a subtype of the parent's return type.
True — this is covariant return, still substitutable because any caller expecting the parent type safely accepts a subtype.
An override may make the method's access more restrictive (public → private).
False — narrowing access breaks substitutability; a caller holding the parent type could suddenly be denied access, so the language forbids it.
Static (class) methods participate in dynamic dispatch.
False — statics are hidden, not overridden; they resolve by declared type at compile time, so no runtime object lookup happens.
Overriding lets you delete the parent's behavior entirely and rely on the child.
True but nuanced — a plain override replaces the body, but if callers depend on the parent's contract you must still honor it; you can replace how, not break what was promised.
If a class overrides equals, it is also overriding a static method.
False —
equals is an instance method; overriding only ever applies to instance methods, so no static is involved.Calling super.method() inside an override is required whenever you override.
False — super is optional; you call it only when you want to extend (reuse parent work) rather than fully replace.
Spot the error
Each statement contains one wrong idea. Name it and correct it.
"To override, the child method just needs the same name as the parent's."
Wrong — same name is not enough; it needs the same full signature (name + parameters), otherwise it becomes an overload and the original still runs for its own argument types.
"Because the variable is typed Shape, the compiler will call Shape.area at runtime."
Wrong — the compiler checks the call against
Shape but does not fix which body runs; at runtime dispatch uses the real object's class, so Circle.area executes."Overloading and overriding are the same feature with two names."
Wrong — overloading varies parameters and is picked at compile time; overriding keeps the signature and is picked at runtime by the object — different mechanisms entirely.
"An override may throw a broader checked exception than the parent declared."
Wrong — that breaks callers written against the parent's contract; an override may throw the same or narrower checked exceptions, never broader.
"A child changing the parameter types is a stronger, stricter override."
Wrong — changing parameters produces a separate overloaded method; the parent's version is not replaced, so the intended override silently never happens (this is why
@Override exists — to catch it)."Since Circle copies Shape's vtable, it must call Shape.area."
Wrong — the child copies the table then overwrites the
area entry with Circle.area; the copy is a starting point, not the final result."Fields are overridden the same way methods are."
Wrong — fields are shadowed/hidden, resolved by the declared type at compile time, with no dynamic dispatch; only instance methods are truly overridden.
Why questions
The answer must explain a mechanism or design reason, not restate the fact.
Why does overriding need the runtime type instead of the declared type?
Because the whole purpose is to let a variable typed as the parent behave as the specific child it holds; using the declared type would collapse every subclass back to the parent's behavior, destroying polymorphism.
Why does the object store a pointer to its own class?
So that even when held in a parent-typed variable, dispatch step one can find the real class and jump to the correct method body — the class pointer travels with the object, the declared type does not.
Why does overriding support the Open/Closed Principle?
New behavior is added by writing a new subclass (extension) instead of editing a central
if/else over types (modification), so existing tested code stays closed to change.Why can't static methods be overridden even though you can redeclare them?
Static calls are bound to the declared type at compile time with no per-object class lookup, so there is no runtime moment where the "real object" could redirect the call — redeclaration merely hides the parent's version.
Why is widening access allowed but narrowing forbidden in an override?
Widening keeps every promise the parent made (anything the parent allowed still works), while narrowing revokes a promise, so a caller relying on the parent's accessibility would break — violating Liskov.
Why is super() sometimes called inside an override?
To extend rather than replace — you run the parent's logic first (reusing its work via super) and then add the child's extra behavior, keeping the code DRY.
Why does covariant return not break callers?
A caller expecting the parent return type can always safely use a returned subtype (a subtype is-a parent), so substitutability holds even though the declared return narrowed.
Why do we call the vtable approach cheaper than repeated if/else type checks?
A vtable is a single constant-time index into a per-class table, whereas an
if/else ladder grows and must be edited with every new type — the table scales for free as subclasses register their own entries.Edge cases
Boundary and degenerate scenarios. Reason through each.
What happens if the parent method is abstract and the child does not override it?
The child stays abstract too and cannot be instantiated; overriding an abstract method is mandatory for a concrete subclass, because there is no parent body to fall back to.
If two subclasses both override area(), and you loop over a mixed list, which runs for each?
Each element dispatches independently on its own runtime class, so
Circle.area runs for circles and Rectangle.area for rectangles within the same loop — no branching needed.Can a child override a method it inherits but that the grandparent (not the parent) actually defined?
Yes — dispatch uses the most-derived definition visible; the vtable already carries the inherited entry, and the child simply overwrites it regardless of which ancestor originally declared it.
What if a child method has the same name and parameters but an incompatible return type (not a subtype)?
It is a compile error, not a valid override — the return type must be the same or a covariant subtype to remain substitutable; an unrelated return type breaks the contract.
What is dispatched when you call an overridden method from inside another parent method?
The most-derived override still runs — because dispatch always uses the actual object's class, even self-calls made from parent code route to the child's version (this can surprise you if the parent method runs during construction).
If a variable is declared as an interface type, which method runs when called?
The concrete implementing object's method runs via dynamic dispatch; the interface only guarantees the method exists to compile the call, never which body executes.
Does overriding still work if the parent method body is empty (a no-op default)?
Yes — an empty parent body is still a real method the child can replace; the vtable entry simply points to the child's body once overwritten, so the empty default is bypassed at runtime.
Recall One-line self-check before you leave
Declared type :::-checks-whether-the-call-compiles; runtime object :::-decides-which-body-runs. If you can say both halves cold, you have beaten this bank.