Register

Java for Beginners

0 of 24 notes read 0%

Reading progress is saved to your INFIRALS account. Sign in to keep this note marked as read.

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.

java
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.

java
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.

java
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.

java
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.

java
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

  1. Create Animal.speak() and override it in Dog with @Override.
  2. Call super.speak() from the dog version and print both lines.
  3. Put a dog and a cat in an Animal[] and call speak in a loop.
  4. Add show(String) to the parent and show(int) to the child. Call both through a parent reference and explain the output.
  5. Remove @Override from 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.

Interview questions

3 questions from this note. Open one to see the answer; no account needed.

  • 01What makes a method an override?

    The child declares the same name and parameter list as an accessible parent method. The return type must be compatible, and `@Override` lets the compiler verify the match.

  • 02How does `super.method()` help?

    It runs the parent implementation from inside the child override, so the child can add work without copying the parent code.

  • 03Why does `animal.purr()` fail when the object is a cat?

    The call is checked against the declared type. `Animal` does not declare `purr`, so a cast to `Cat` is needed before the call.