Skip to main content

Method Overriding

Reading Time: 9 Minutes Difficulty: Beginner


Topic Summary​

Method overriding happens when a child class provides its own specific version of a method that already exists in the parent class. The child says: "I know you (parent) have a speak() method, but I'm going to do it MY way." This is the heart of runtime polymorphism - Java decides which version to call based on the actual object type, not the variable type.


What You'll Learn​

  • What method overriding is and why it's powerful
  • The rules that a valid override must follow
  • The @Override annotation and why you should use it
  • What covariant return type means
  • Which methods cannot be overridden

Prerequisites​

  • Inheritance (Lesson 05)
  • super Keyword (Lesson 06)

Explanation​

What Is Method Overriding?​

When a child class defines a method with the exact same name, exact same parameters, and a compatible return type as a method in its parent class, it overrides that method.

class Animal {
void speak() {
System.out.println("Some animal makes a sound.");
}
}

class Dog extends Animal {
@Override
void speak() { // OVERRIDES Animal's speak()
System.out.println("Dog says: Woof!");
}
}

class Cat extends Animal {
@Override
void speak() { // OVERRIDES Animal's speak()
System.out.println("Cat says: Meow!");
}
}

When you call speak() on a Dog, Java runs Dog's version. When you call it on a Cat, Java runs Cat's version. The parent's generic version is replaced by the child's specific one.


The Rules for Valid Overriding​

For an override to be valid, ALL of these must be true:

RuleExplanation
Same method nameMust be exactly the same name
Same parameter listSame number, types, and order of parameters
Same or covariant return typeSame return type, OR a subtype (child type)
Same or less restrictive accessCan make it more public, never more private
Cannot throw broader exceptionsCan throw fewer/narrower checked exceptions

The @Override Annotation​

@Override is an annotation you place above an overriding method. It tells the Java compiler: "I intend to override a parent method here - please verify this is correct."

@Override
void speak() {
System.out.println("Dog says: Woof!");
}

Why use it?

  • If you accidentally misspell the method name or change parameters, the compiler throws an error
  • Without it, Java would silently treat it as a new method, not an override!
class Dog extends Animal {
// TYPO! This is NOT overriding - it's a new method called 'speek'
void speek() { // Without @Override, no error!
System.out.println("Dog says: Woof!");
}
}

Always use @Override - it's free protection.


Runtime Polymorphism (Dynamic Method Dispatch)​

This is where the magic happens. When you have a parent type reference pointing to a child object, Java decides which method to call at runtime (not compile time), based on the actual object:

Animal a = new Dog(); // Parent reference, Child object
a.speak(); // Which speak() is called? → Dog's! (decided at runtime)

This is called dynamic method dispatch. Java looks at the actual object type (Dog), not the reference type (Animal), to pick the method.

Animal[] animals = {
new Dog(),
new Cat(),
new Animal()
};

for (Animal a : animals) {
a.speak(); // Each calls its OWN version - resolved at runtime
}

Output:

Dog says: Woof!
Cat says: Meow!
Some animal makes a sound.

Covariant Return Type​

Since Java 5, an overriding method can return a subtype of the parent's return type. This is called covariant return type.

class Animal {
Animal create() {
return new Animal();
}
}

class Dog extends Animal {
@Override
Dog create() { // Returns Dog (subtype of Animal) - valid override!
return new Dog();
}
}

Methods That CANNOT Be Overridden​

ModifierReason Cannot Override
privateNot visible to child class at all
staticBelongs to the class, not objects; only hidden, not overridden
finalExplicitly marked "do not override"
class Parent {
private void secret() { ... } // can't override - child can't see it
static void classMethod() { ... } // can't override - only hide
final void lockedMethod() { ... } // can't override - compiler error
}

Real-World Analogy​

Think of a restaurant franchise.

The parent company (Animal) has a standard recipe for "Burger" that all restaurants should have. But each specific location (Dog, Cat) can override that standard recipe with their local specialty version. When a customer walks into a specific location and orders a "Burger," they get that location's version - not the generic headquarters version.

That's method overriding: the same "menu item" name, but each subclass serves its own version.


Code Example​

// Parent class
class Shape {
String color;

Shape(String color) {
this.color = color;
}

// This will be overridden
double area() {
return 0.0;
}

void displayInfo() {
System.out.println("Color: " + color + ", Area: " + area());
}
}

// Circle overrides area()
class Circle extends Shape {
double radius;

Circle(String color, double radius) {
super(color);
this.radius = radius;
}

@Override
double area() {
return Math.PI * radius * radius;
}
}

// Rectangle overrides area()
class Rectangle extends Shape {
double width, height;

Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}

@Override
double area() {
return width * height;
}
}

// Triangle overrides area()
class Triangle extends Shape {
double base, height;

Triangle(String color, double base, double height) {
super(color);
this.base = base;
this.height = height;
}

@Override
double area() {
return 0.5 * base * height;
}
}

public class Main {
public static void main(String[] args) {
// Parent references, child objects - runtime polymorphism!
Shape[] shapes = {
new Circle("Red", 5),
new Rectangle("Blue", 4, 6),
new Triangle("Green", 3, 8)
};

for (Shape s : shapes) {
s.displayInfo(); // Each calls ITS OWN area() - resolved at runtime
}
}
}

Output​

Color: Red, Area: 78.53981633974483
Color: Blue, Area: 24.0
Color: Green, Area: 12.0

Common Mistakes​

  • Mistake: Not using @Override and accidentally creating a new method with a typo → Fix: Always add @Override - the compiler will catch naming mistakes.
  • Mistake: Trying to override a private or final method → Fix: private methods are invisible to child classes; final methods are locked. Neither can be overridden.
  • Mistake: Confusing overriding with overloading → Fix: Overriding = same method signature in child class. Overloading = same method name with different parameters in the SAME class.

Best Practices​

  • Always use @Override when overriding - treat it as mandatory, not optional
  • Call super.method() when you want to extend (not replace) the parent's behavior
  • Don't override just to do nothing - if a method makes no sense in the child, reconsider the design
  • Keep overriding methods focused - they should do what their name says

Interview Questions​

Q: What is method overriding in Java? A: Method overriding is when a child class provides its own implementation for a method that is already defined in its parent class. The overriding method must have the same name, same parameter list, and a compatible return type. It enables runtime polymorphism.

Q: What is dynamic method dispatch? A: Dynamic method dispatch (runtime polymorphism) is when Java determines which overridden method to call at runtime based on the actual object type, not the reference type. If Animal a = new Dog() and you call a.speak(), Java calls Dog's speak() because the actual object is a Dog.

Q: Can you override a static method in Java? A: No. Static methods belong to the class, not to objects. A child class can define a static method with the same name (called method hiding), but this is not true overriding - dynamic dispatch does not apply to static methods.


Quick Revision​

Override = child provides its own version of a parent method Rules: same name, same parameters, same or covariant return type Use @Override - it's your safety net against typos Runtime polymorphism: Java picks the method based on actual object type Cannot override private, static, or final methods


  • Method Overloading
  • Polymorphism
  • Abstraction
  • Interfaces

Next Lesson​

08 - Method Overloading