Overriding methods and using super
Inheritance, interfaces and polymorphism11 min readLesson 2 of 6
2.1 Replacing inherited behavior
Overriding declares a method in a child class with the same name and parameter list as a method in the parent. When the child object receives that call, the child version runs. The return type must be the same or a subtype, and the child method cannot reduce visibility.
Add @Override above the method. The annotation is optional, but the compiler then checks that a parent method really matches. Without it, a typo in the parameter list creates a different overload.
The method signature must match exactly. speak() in the parent and speak(String) in the child are different methods, so no override occurs.
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.speak();
}
}
class Animal {
void speak() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Woof");
}
}Output
Woof
2.1.1 Calling the parent version
super.method(...) calls the parent implementation from inside the override. This is useful when the child adds a step and keeps the parent's work. The parent method runs at that point, then the child continues.
A common pattern prints a shared line from the parent and then a child-specific line. The child does not copy the parent's code, so a later change to the parent applies to both.
super(...) with parentheses calls a parent constructor, while super.method(...) with a dot calls a method. The two forms look similar and do different jobs.
public class Main {
public static void main(String[] args) {
SavingsAccount account = new SavingsAccount();
account.describe();
}
}
class Account {
String type = "Account";
void describe() {
System.out.println("Type: " + type);
}
}
class SavingsAccount extends Account {
@Override
void describe() {
super.describe();
System.out.println("Interest: 4%");
}
}Output
Type: Account Interest: 4%
2.1.2 Dynamic dispatch
The object decides which override runs, whatever the variable type says. Animal animal = new Cat(); animal.speak(); runs Cat.speak() because the object is a cat. This is dynamic dispatch, and it is the mechanism behind polymorphism.
A loop over an array of the parent type can call one method name and let each object supply its own behavior. Adding a new subclass does not require editing the loop.
The compiler still checks the call against the variable type. animal.purr() fails if purr is declared only in Cat, even when the object really is a cat. A cast makes that call possible, with a runtime check.
public class Main {
public static void main(String[] args) {
Animal animal = new Cat();
animal.speak();
System.out.println(animal.getClass().getSimpleName());
}
}
class Animal {
void speak() { System.out.println("..."); }
}
class Cat extends Animal {
@Override
void speak() { System.out.println("Meow"); }
}Output
Meow Cat
2.1.3 Overloads are different
An overload has the same name and a different parameter list. Overloads are chosen at compile time from the static types of the arguments. Overrides are chosen at runtime from the object's class.
A child method show(int) does not override a parent show(String). It overloads the name. Calling show("text") through a parent reference runs the parent method, because the argument type matches the parent signature.
This difference explains why @Override is worth adding. The annotation turns a silent overload into a compiler error when the intended parent method does not exist.
public class Main {
public static void main(String[] args) {
Parent parent = new Child();
parent.show("text");
Child child = new Child();
child.show(5);
child.show("text");
}
}
class Parent {
void show(String text) { System.out.println("Parent " + text); }
}
class Child extends Parent {
void show(int number) { System.out.println("Child " + number); }
}Output
Parent text Child 5 Parent text
2.1.4 Rules and limits
A method declared final cannot be overridden. A static method is hidden by a subclass method with the same signature, and the call is resolved from the type name. A private method is not visible to the child, so a same-name method there is a new method, not an override.
Access can stay the same or become more open. A protected parent method can become public in the child, but a public parent method cannot become protected.
Constructors are not overridden because they are not inherited. A child constructor can call a parent constructor with super(...), as the previous lesson showed.
public class Main {
public static void main(String[] args) {
new Manager("Ravi", 4).describe();
}
}
class Employee {
String name;
Employee(String name) { this.name = name; }
void describe() { System.out.println(name + " works"); }
}
class Manager extends Employee {
int reports;
Manager(String name, int reports) { super(name); this.reports = reports; }
@Override
void describe() {
super.describe();
System.out.println(name + " leads " + reports);
}
}Output
Ravi works Ravi leads 4
2.1.5 Practice
- Create
Animal.speak()and override it inDogwith@Override. - Call
super.speak()from the dog version and print both lines. - Put a dog and a cat in an
Animal[]and callspeakin a loop. - Add
show(String)to the parent andshow(int)to the child. Call both through a parent reference and explain the output. - Remove
@Overridefrom a correct override and check that it still compiles. Then misspell the method name with the annotation restored and read the compiler error.
For step 4, the argument type at the call site decides which overload is available, before the object type matters.
