Method Overriding

IB Syllabus: B3.2 (HL): Override methods in subclasses to provide specialised behaviour.

HL Only: This page covers content assessed at HL level only.


Key Concepts

What is Method Overriding?

When a child class defines a method with the same name, same parameters, and same return type as a method in its parent class, the child’s version replaces the parent’s version for objects of the child type. This is method overriding.

public class Animal {
    public String speak() {
        return "...";
    }
}

public class Dog extends Animal {
    @Override
    public String speak() {
        return "Woof!";
    }
}

public class Cat extends Animal {
    @Override
    public String speak() {
        return "Meow!";
    }
}
class Animal():
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):    # same name, same parameters - this IS the override
        return "Woof!"  # (Python has no @Override annotation)

class Cat(Animal):
    def speak(self):
        return "Meow!"
Dog d = new Dog();
Cat c = new Cat();
Animal a = new Animal();

System.out.println(d.speak());  // Woof!
System.out.println(c.speak());  // Meow!
System.out.println(a.speak());  // ...
d = Dog()
c = Cat()
a = Animal()

print(d.speak())    # Woof!
print(c.speak())    # Meow!
print(a.speak())    # ...

Each subclass provides its own version of speak(). When you call speak() on a Dog, Java uses the Dog’s version, not the Animal’s.

The @Override Annotation

@Override is an annotation placed above a method to tell the compiler “I intend to override a parent method.” It is optional but strongly recommended because:

  1. If you misspell the method name, the compiler catches it immediately
  2. If you accidentally change the parameter types, the compiler warns you
  3. It makes the code easier to read: anyone reading the class knows this method replaces a parent’s version
@Override
public String speak() {   // compiler checks: does Animal have speak()?
    return "Woof!";
}
def speak(self):    # Python has no @Override - redefining the method is the override
    return "Woof!"
# Watch out: a typo like speek() silently creates a NEW method - Python gives no warning

Without @Override, a typo like speek() would create a NEW method instead of overriding the parent’s speak(). The code would compile but behave incorrectly: a subtle bug. (In Python this trap is worse: there is no annotation to catch the typo, so check spelling carefully.)

Always use @Override when overriding a method. It costs nothing and prevents hard-to-find bugs. IB mark schemes expect to see it.

Overriding Rules

For a method to override a parent method, it must have:

  1. Same method name as the parent
  2. Same parameter list (number, types, and order)
  3. Same return type (or a subtype)
  4. Same or less restrictive access modifier (e.g., a protected parent method can be overridden as public, but not as private)

If any of these differ, it is not an override: it is a new method (overloading or a completely separate method).

Overriding vs Overloading

These are often confused but are fundamentally different:

  Overriding Overloading
Where Child class redefines a parent method Same class defines multiple methods with the same name
Parameters Must be identical to the parent Must be different (different number or types)
Return type Must be the same (or subtype) Can be different
Which runs? Decided at runtime (based on actual object type) Decided at compile time (based on parameter types)
Purpose Specialise behaviour for a subtype Provide multiple ways to call the same operation
// OVERLOADING -- same class, different parameters
public class Calculator {
    public int add(int a, int b) { return a + b; }
    public double add(double a, double b) { return a + b; }
    public int add(int a, int b, int c) { return a + b + c; }
}

// OVERRIDING -- child class, same method signature
public class Animal {
    public String speak() { return "..."; }
}
public class Dog extends Animal {
    @Override
    public String speak() { return "Woof!"; }  // same name, same params
}
# OVERLOADING does not exist in Python - writing add() twice keeps only the LAST version.
# One method handles both ints and decimals (Python checks types at runtime):
class Calculator():
    def add(self, a, b):
        return a + b
    # a three-number version would need a different name, e.g. add_three(a, b, c)

# OVERRIDING works exactly as in Java -- child class, same method name
class Animal():
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):    # same name, no parameters - replaces the parent's version
        return "Woof!"

Calling the Parent’s Version with super

Sometimes you want to extend the parent’s behaviour rather than completely replace it. Use super.methodName() to call the parent’s version from inside the override:

public class Employee {
    private String name;
    private double baseSalary;

    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    public double calculatePay() {
        return baseSalary;
    }

    public String toString() {
        return name + ": $" + calculatePay();
    }
}

public class Manager extends Employee {
    private double bonus;

    public Manager(String name, double baseSalary, double bonus) {
        super(name, baseSalary);
        this.bonus = bonus;
    }

    @Override
    public double calculatePay() {
        return super.calculatePay() + bonus;  // parent's pay + bonus
    }
}
class Employee():
    def __init__(self, name, base_salary):
        self._name = name
        self._base_salary = base_salary

    def calculate_pay(self):
        return self._base_salary

    def __str__(self):    # __str__ is Python's toString()
        return self._name + ": $" + str(self.calculate_pay())

class Manager(Employee):
    def __init__(self, name, base_salary, bonus):
        super().__init__(name, base_salary)    # call the parent constructor
        self._bonus = bonus

    def calculate_pay(self):
        return super().calculate_pay() + self._bonus    # parent's pay + bonus
Employee e = new Employee("Alice", 50000);
Manager m = new Manager("Bob", 60000, 10000);

System.out.println(e.calculatePay());  // 50000.0
System.out.println(m.calculatePay());  // 70000.0 (60000 + 10000)
e = Employee("Alice", 50000)
m = Manager("Bob", 60000, 10000)

print(e.calculate_pay())    # 50000 (Java's double prints 50000.0; a Python int stays 50000)
print(m.calculate_pay())    # 70000 (60000 + 10000)

The Manager’s calculatePay() reuses the parent’s calculation via super.calculatePay() and adds the bonus on top. This avoids duplicating the base salary logic.

toString() Override

Every Java class inherits a toString() method from Object (the root of all Java classes). By default, it returns something unhelpful like Student@1a2b3c. Overriding it lets you define a meaningful string representation:

public class Student {
    private String name;
    private int grade;

    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return name + " (Grade " + grade + ")";
    }
}
class Student():
    def __init__(self, name, grade):
        self._name = name
        self._grade = grade

    def __str__(self):    # __str__ is Python's toString()
        return self._name + " (Grade " + str(self._grade) + ")"
Student s = new Student("Alice", 11);
System.out.println(s);  // Alice (Grade 11)
// Without override: Student@7a81197d
s = Student("Alice", 11)
print(s)    # Alice (Grade 11)
# Without __str__: something like <__main__.Student object at 0x1024b5d90>

System.out.println() automatically calls toString() on objects. Overriding it makes debugging and output much easier. (Python’s print() calls __str__ in exactly the same way.)


Worked Examples

Example 1: Shape Hierarchy

public class Shape {
    public double getArea() {
        return 0;
    }

    public String describe() {
        return "Shape with area " + getArea();
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width, height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double getArea() {
        return width * height;
    }
}
import math

class Shape():
    def get_area(self):
        return 0

    def describe(self):
        return "Shape with area " + str(self.get_area())

class Circle(Shape):
    def __init__(self, radius):
        self._radius = radius

    def get_area(self):
        return math.pi * self._radius * self._radius    # math.pi is Java's Math.PI

class Rectangle(Shape):
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_area(self):
        return self._width * self._height
Circle c = new Circle(5);
Rectangle r = new Rectangle(4, 6);

System.out.println(c.describe());  // Shape with area 78.53981633974483
System.out.println(r.describe());  // Shape with area 24.0
c = Circle(5)
r = Rectangle(4, 6)

print(c.describe())    # Shape with area 78.53981633974483
print(r.describe())    # Shape with area 24 (ints multiply to an int; Java's double shows 24.0)

Notice: describe() is NOT overridden: it is inherited from Shape. But it calls getArea(), which IS overridden. So when c.describe() runs, it calls the Circle’s getArea(), not the Shape’s. This is the power of overriding.


Quick Check

Q1. What is method overriding?

Q2. Why should you use @Override?

Q3. A parent has void print(String s). A child defines void print(int n). Is this overriding?

Q4. What does super.calculatePay() do inside an overridden method?


Spot the Error

A student wants Dog to override Animal's speak() method, but calling d.speak() still returns "..." (the parent's version). Click the buggy line, then pick the fix.

1public class Dog extends Animal { 2 @Override 3 public String speak(String sound) { 4 return "Woof!"; 5 } 6}

Pick the fix:


Predict the Output

Using the Employee/Manager classes from the Key Concepts section:

Manager m = new Manager("Bob", 60000, 10000);
System.out.println(m.calculatePay());

Using the Shape/Circle classes from Example 1, what does c.describe() start with?

Circle c = new Circle(5);
System.out.println(c.describe());

Does the output start with "Shape" or "Circle"?


Practice Exercises

Core

  1. Override toString(): Create a Book class with title and author. Override toString() to return "Title by Author". Create a Book and print it directly with System.out.println(book).

  2. Identify override vs overload: For each pair, state whether the child method overrides or overloads the parent:

    • (a) Parent: void draw(), Child: void draw()
    • (b) Parent: int calculate(int x), Child: int calculate(int x, int y)
    • (c) Parent: String getName(), Child: String getName()
    • (d) Parent: void print(String s), Child: void print(int n)

Extension

  1. Extend with super: Create a Vehicle class with getDescription() returning "Vehicle: [make]". Create a Car subclass that overrides getDescription() to return the parent’s description plus " (4 doors)". Use super.getDescription().

  2. The describe() trick: In Example 1, Shape.describe() calls getArea(). Explain why c.describe() (where c is a Circle) uses Circle’s getArea() even though describe() is defined in Shape. What OOP principle makes this work?

Challenge

  1. Payment system: Create a class hierarchy: Employee (base salary), Manager (base + bonus), SalesPerson (base + commission percentage * sales total). All override calculatePay(). Create an array of Employees, fill it with a mix of types, and calculate the total payroll.

Connections

  • Prerequisites: Inheritance: must understand extends and super before overriding
  • Next: Polymorphism: overriding enables polymorphic behaviour
  • Related: Constructors: constructor overloading vs method overriding

© EduCS.me — A resource hub for Computer Science education

This site uses Just the Docs, a documentation theme for Jekyll.