Chapter 35 of 57

Method Overriding in Java

When we use inheritance, a child class automatically gets the methods of its parent class. But sometimes the child class needs to provide its own version of an inherited method.

This is called method overriding.

For example, suppose we have an Animal class with a sound() method:

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

Now we create a Dog class that inherits from Animal:

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

Here, Dog is overriding the sound() method that it inherited from Animal.

Why Do We Need Method Overriding?

Different child classes may need to behave differently even though they share a common method.

For example, all animals can make a sound, but different animals make different sounds.

We can create:

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

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows.");
    }
}

Now each class has its own implementation of sound().

Dog dog = new Dog();
dog.sound();

Cat cat = new Cat();
cat.sound();

Output:

Dog barks.
Cat meows.

The method name remains the same, but the behavior is different.

The @Override Annotation

You may have noticed:

@Override
void sound() {
    System.out.println("Dog barks.");
}

@Override is an annotation that tells Java:

"I am intentionally overriding a method from the parent class."

It isn't strictly required in every case, but using it is strongly recommended. If you accidentally make a mistake in the method name or parameters, Java can point it out.

For example, if the parent has:

void sound()

but you accidentally write:

@Override
void sounds() {
    // ...
}

Java will give an error because there is no sounds() method in the parent class to override.

Rules of Method Overriding

For a method to be overridden, the child class needs to provide a method with the same method name and parameter list as the parent method.

For example:

class Animal {
    void sound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog bark");
    }
}

Both methods have:

Name: sound
Parameters: none

so the child method overrides the parent method.

The return type also needs to be compatible with the parent's return type.

Overriding vs Overloading

Method overriding and method overloading are easy to confuse because their names sound similar.

Method overloading happens when methods have the same name but different parameter lists, usually within the same class.

void add(int a, int b) {
}

void add(int a, int b, int c) {
}

Method overriding happens when a child class provides a new implementation of a method inherited from its parent.

class Animal {
    void sound() {
    }
}

class Dog extends Animal {
    @Override
    void sound() {
    }
}

So remember:

Overloading → same method name, different parameters

Overriding → child class changes the inherited method's behavior

Using super with Overriding

Sometimes the child class wants to add its own behavior but still wants to execute the parent's version of the method.

We can use super for this.

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

class Dog extends Animal {
    @Override
    void sound() {
        super.sound();
        System.out.println("Dog barks.");
    }
}

Now:

Dog dog = new Dog();
dog.sound();

Output:

Animal makes a sound.
Dog barks.

Here:

super.sound();

calls the parent version, and then the child adds its own behavior.

Method Overriding with Parent References

One of the most important uses of method overriding appears when we use a parent class reference to refer to a child object.

For example:

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

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

Now:

Animal animal = new Dog();

animal.sound();

Output:

Dog barks.

Even though the reference type is Animal, the actual object is a Dog, so Java calls the overridden Dog version of sound().

This behavior is an important part of polymorphism, which we'll explore later.

A Real-World Example

Imagine we have a general Vehicle class:

class Vehicle {
    void start() {
        System.out.println("Vehicle is starting.");
    }
}

A car and a motorcycle may both be vehicles, but they can have different ways of starting.

class Car extends Vehicle {
    @Override
    void start() {
        System.out.println("Car starts with a key.");
    }
}

class Motorcycle extends Vehicle {
    @Override
    void start() {
        System.out.println("Motorcycle starts with a button.");
    }
}

Now:

Car car = new Car();
Motorcycle motorcycle = new Motorcycle();

car.start();
motorcycle.start();

Output:

Car starts with a key.
Motorcycle starts with a button.

Both classes inherited start() from Vehicle, but each class provided its own implementation.

The main idea behind method overriding is simple: inherit a method from the parent, but change its behavior in the child class when the child needs to behave differently.