Unit 3 · OOP Concepts

3.5.4Method overriding

Overriding means a subclass provides its own version of a method already defined in the superclass, with the same name and parameter list. The return type must be the same, or a subtype of the original (a covariant return type), and the method cannot be made less visible (a public method must stay public). Overriding gives run-time polymorphism.

Java
class Animal {
    void sound() { System.out.println("Some sound"); }
}
class Dog extends Animal {
    @Override
    void sound() { System.out.println("Woof"); }
}
class Cat extends Animal {
    @Override
    void sound() { System.out.println("Meow"); }
}

Use @Override so the compiler checks that you really are overriding a parent method. Use super.sound() to call the parent's version from inside the overriding method. Methods marked final, static or private cannot be overridden (a static method with the same signature only hides the parent's).

OverloadingOverriding
Same classSubclass and superclass
Same name, different parametersSame name, same parameters
Decided at compile timeDecided at run time
Return type may differSame return type, or a subtype (covariant)
Any access modifierCannot be more restrictive than the parent's method

How Java decides which version runs

When you call a method on an object, the JVM looks at the object's actual class at run time and starts searching there. If that class has its own version of the method, that version runs. If not, the search moves up to the parent, then the grandparent, and so on up to Object.

Java
Animal a = new Dog();
a.sound();    // Woof: the object is a Dog, so Dog's sound() runs
Animal b = new Animal();
b.sound();    // Some sound

The variable type (Animal) only decides whether the call is allowed. The object type (Dog) decides which code runs. This is called dynamic binding, and it is how run-time polymorphism works (see 3.6).

Why @Override matters

@Override is optional, but you should always use it. Without it, a small typing mistake silently creates a new method instead of overriding:

Java
class Dog extends Animal {
    void Sound() { System.out.println("Woof"); }   // capital S: a new method!
}
new Dog().sound();    // prints "Some sound", not "Woof"

With @Override on that method, the compiler reports Sound() in Dog does not override or implement a method from a supertype, and you find the mistake at once.

Overriding toString() and equals()

Two methods from Object are overridden in almost every real class:

Java
class Student {
    private String id;
    private String name;

    Student(String id, String name) { this.id = id; this.name = name; }

    @Override
    public String toString() {                 // used by println
        return "Student " + id + ": " + name;
    }

    @Override
    public boolean equals(Object o) {          // same values, not same object
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student other = (Student) o;
        return id.equals(other.id);
    }

    @Override
    public int hashCode() { return id.hashCode(); }   // always override with equals
}

System.out.println(new Student("S01", "Nimal"));   // Student S01: Nimal

Both methods are public in Object, so the overriding versions must also be public. Remember the rule: an override cannot be less visible than the original.

Remember

  • Overriding: the same signature in a subclass; the actual object's version runs.
  • Always write @Override.
  • Use super.method() to reuse the parent's version inside an override.