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.
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).
| Overloading | Overriding |
|---|---|
| Same class | Subclass and superclass |
| Same name, different parameters | Same name, same parameters |
| Decided at compile time | Decided at run time |
| Return type may differ | Same return type, or a subtype (covariant) |
| Any access modifier | Cannot 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.
Animal a = new Dog();
a.sound(); // Woof: the object is a Dog, so Dog's sound() runs
Animal b = new Animal();
b.sound(); // Some soundThe 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:
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:
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: NimalBoth 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.