Method Overriding Rules in Java
Fast recall
Quick revision
Core answer
@Override so the compiler checks the intent.Knowledge of all the overriding rules, particularly covariant return types, access modifier widening, and exception rules
Not knowing about covariant return types, or thinking you can narrow the access modifier (e.g., parent public to child private)
Mention @Override's role in catching typos and the covariant return type feature added in Java 5
Interview-ready guide
Interview answer
For overriding to count, Java enforces five rules, all there to keep a child usable wherever the parent is expected:
- First, the signature has to match exactly — same name, same parameters; change a parameter and you've quietly written a new overloaded method instead, while the parent's version still runs.
- Second, the return type must be the same or a subtype — covariant returns, so if the parent returns
Animal, the child can returnDog. - Third, access can only widen —
protectedtopublicis fine, butpublicdown toprivatewon't compile, since a caller holding the parent type expects at least the access the parent promised. - Fourth, checked exceptions can only narrow — the child can throw the same, something narrower, or nothing, but never a broader checked exception; unchecked ones are exempt.
- Fifth, some methods can't be overridden at all —
privateisn't inherited,finalis locked, and astaticmethod with the same name is method hiding, not overriding.
The point I always stress is @Override. It's optional, but it's the cheapest safety net in Java: without it, a typo in the name compiles fine and silently creates a new method, so your override never runs and the bug surfaces only at runtime. So my rule is simple — annotate every override.
Method Overriding Rules in Java
Direct Answer
An overriding method has the same signature (after type erasure), a compatible covariant return type, and no more restrictive access. It cannot add a broader checked exception than the overridden contract allows. Private and final methods are not overridable; a same-signature static method is hidden, not overridden. Use @Override so the compiler checks the intent.
Code Example
class Animal {
// Base method: protected, returns Animal, throws Exception
protected Animal reproduce() throws Exception {
return new Animal();
}
public String speak() {
return "...";
}
public final void breathe() {
System.out.println("Breathing");
}
}
class Dog extends Animal {
// ── VALID overrides ────────────────────────────────────────────────────
@Override
public Dog reproduce() throws IllegalStateException {
// public is MORE permissive than protected — valid (Rule 3)
// Dog is covariant subtype of Animal — valid return type (Rule 2)
// IllegalStateException is unchecked — unchecked exceptions are not restricted (Rule 4)
return new Dog();
}
@Override
public String speak() {
return "Woof!"; // same signature, same return type — valid (Rules 1 & 2)
}
// ── INVALID overrides (commented out — would not compile) ──────────────
// @Override
// private String speak() { return "Woof!"; } // ERROR: narrowing access public→private (Rule 3)
// @Override
// public void breathe() { } // ERROR: cannot override final method (Rule 5)
// @Override
// protected Animal reproduce() throws Exception { return new Dog(); } // ERROR if we tried to throw
// a checked exception not declared in parent — see Rule 4
}
public class OverridingDemo {
public static void main(String[] args) throws Exception {
Animal a = new Dog(); // parent reference, child object
System.out.println(a.speak()); // Output: Woof! ← Dog.speak() via dynamic dispatch
System.out.println(a.reproduce()); // Output: Dog@... ← Dog.reproduce() at runtime
}
}
// Output:
// Woof!
// Dog@<hashcode>
Common Mistakes & Pitfalls
- Skipping
@Override— a typo in the name or a wrong parameter silently creates a new method; your override never runs and polymorphism quietly breaks with no compiler warning. - Narrowing the access modifier — going from
publicin the parent toprivatein the child is a compile error; you can only keep or widen access. - Throwing a broader checked exception — if the parent throws
IOException, the child can throwFileNotFoundExceptionor nothing, but neverException. - Believing static methods can be overridden — redeclaring a
staticmethod in a child is method hiding, resolved by the reference type, not dynamic dispatch.
Check your understanding
Answer these without looking back at the article.
- What is a covariant return type in Java?
- Why can't you narrow the access modifier when overriding?
- What happens if you use @Override on a method that doesn't actually override anything?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.