20. Method Overriding
Overriding lets a subclass provide a specific implementation of a method declared in a superclass; the selection happens at runtime (dynamic dispatch), not compile time. @Override is mandatory best practice.
Explanation
When you override a method, the JVM uses the actual runtime type of the object to select which implementation to call โ not the declared type of the reference. This is the core mechanism of polymorphism.
Rules for a valid override: the method signature (name + parameter types) must match exactly. The return type must be the same or a covariant (subtype) return. Access cannot be made more restrictive (you can widen from protected to public, not narrow from public to protected). The overriding method cannot declare new checked exceptions not declared in the parent.
Always use @Override โ it causes a compile error if the method does not actually override anything (typo protection). final methods cannot be overridden. static methods are not overridden; they are hidden (resolved at compile time based on reference type).
Example
public class MethodOverriding {
static class Shape {
public double area() { return 0.0; }
// Covariant return: overrider can return a subtype
public Shape copy() { return new Shape(); }
// final: cannot be overridden
public final String type() { return "Shape"; }
}
static class Circle extends Shape {
private final double radius;
Circle(double radius) { this.radius = radius; }
@Override // compile error if signature doesn't match โ safety net!
public double area() { return Math.PI * radius * radius; }
@Override
public Circle copy() { return new Circle(radius); } // covariant return OK
}
static class Square extends Shape {
private final double side;
Square(double side) { this.side = side; }
@Override
public double area() { return side * side; }
}
public static void main(String[] args) {
Shape[] shapes = { new Circle(5), new Square(4) };
for (Shape s : shapes) {
// Runtime dispatch: Circle.area() or Square.area() selected at runtime
System.out.printf("%s area = %.2f%n", s.getClass().getSimpleName(), s.area());
}
// Static methods are hidden, not overridden
Shape ref = new Circle(1);
System.out.println(ref.type()); // "Shape" (final โ cannot override)
// Covariant return: copy() on Circle reference returns Circle
Circle c = new Circle(3);
Circle copy = c.copy(); // no cast needed โ covariant return
System.out.printf("Copy area: %.2f%n", copy.area());
}
}Sources
- https://docs.oracle.com/javase/tutorial/java/IandI/override.html โ Overriding and hiding methods
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Override.html โ @Override annotation