Overriding Methods

Introduction

Method Overriding occurs when a subclass provides a specific implementation of a method already defined in its parent class.


Basic Concept

class Parent {
    void display() {
        System.out.println("Parent display");
    }
}

class Child extends Parent {
    @Override  // Annotation (optional but recommended)
    void display() {
        System.out.println("Child display");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Parent();
        p.display();  // Parent display

        Child c = new Child();
        c.display();  // Child display (overridden)
    }
}

Rules for Method Overriding

  1. Same method signature (name + parameters)
  2. Same or covariant return type
  3. Cannot reduce access (can increase)
  4. Cannot throw new checked exceptions
  5. Must use @Override annotation (recommended)
  6. Method must be inherited (not private)
  7. static methods cannot be overridden
  8. final methods cannot be overridden

Simple Example

class Animal {
    void sound() {
        System.out.println("Animal makes sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.sound();  // Animal makes sound

        Dog d = new Dog();
        d.sound();  // Dog barks

        Cat c = new Cat();
        c.sound();  // Cat meows
    }
}

Using super to Call Parent Method

class Parent {
    void display() {
        System.out.println("Parent display");
    }
}

class Child extends Parent {
    @Override
    void display() {
        super.display();  // Call parent method first
        System.out.println("Child display");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.display();
    }
}

Output:

Parent display
Child display

Complete Example

class BankAccount {
    protected double balance;

    void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited: " + amount);
        System.out.println("Balance: " + balance);
    }

    void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount);
        } else {
            System.out.println("Insufficient balance");
        }
    }
}

class SavingsAccount extends BankAccount {
    private double minimumBalance = 1000;

    @Override
    void withdraw(double amount) {
        if (balance - amount >= minimumBalance) {
            super.withdraw(amount);  // Call parent withdraw
        } else {
            System.out.println("Cannot withdraw. Minimum balance required: " + minimumBalance);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        SavingsAccount acc = new SavingsAccount();
        acc.deposit(5000);
        acc.withdraw(3000);  // Not allowed (below minimum)
        acc.withdraw(2000);  // Allowed
    }
}

Access Modifier Rules

Can Increase Access:

class Parent {
    protected void method() { }  // protected
}

class Child extends Parent {
    @Override
    public void method() { }     // ✓ OK: increased to public
}

Cannot Reduce Access:

class Parent {
    public void method() { }     // public
}

class Child extends Parent {
    @Override
    protected void method() { }  // ✗ Error: reduced to protected
}

Return Type Rules

Same Return Type:

class Parent {
    int getValue() {
        return 10;
    }
}

class Child extends Parent {
    @Override
    int getValue() {  // ✓ Same return type
        return 20;
    }
}

Covariant Return Type:

class Animal { }
class Dog extends Animal { }

class Parent {
    Animal getAnimal() {
        return new Animal();
    }
}

class Child extends Parent {
    @Override
    Dog getAnimal() {  // ✓ Covariant return type (subclass)
        return new Dog();
    }
}

@Override Annotation

class Parent {
    void display() { }
}

class Child extends Parent {
    @Override  // Recommended
    void display() { }  // Compiler checks if this actually overrides
}

Benefits:

  1. Compile-time check: Ensures method actually overrides
  2. Prevents typos: Catches spelling mistakes
  3. Documentation: Clear intent

Without @Override:

class Parent {
    void display() { }
}

class Child extends Parent {
    void Display() { }  // Typo! This is a NEW method, not override
}

With @Override:

class Child extends Parent {
    @Override
    void Display() { }  // ✗ Compiler error: method doesn't override
}

Static Methods Cannot Be Overridden

class Parent {
    static void display() {
        System.out.println("Parent static");
    }
}

class Child extends Parent {
    static void display() {  // This is method HIDING, not overriding
        System.out.println("Child static");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent.display();  // Parent static
        Child.display();   // Child static

        Parent p = new Child();
        p.display();  // Parent static (not polymorphic)
    }
}

Real-World Example

class Shape {
    String color;

    double area() {
        return 0;
    }

    void display() {
        System.out.println("Color: " + color);
        System.out.println("Area: " + area());
    }
}

class Circle extends Shape {
    double radius;

    Circle(String color, double radius) {
        this.color = color;
        this.radius = radius;
    }

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

class Rectangle extends Shape {
    double length, width;

    Rectangle(String color, double length, double width) {
        this.color = color;
        this.length = length;
        this.width = width;
    }

    @Override
    double area() {
        return length * width;
    }
}

public class Main {
    public static void main(String[] args) {
        Circle c = new Circle("Red", 5);
        c.display();

        System.out.println();

        Rectangle r = new Rectangle("Blue", 4, 6);
        r.display();
    }
}

Overriding vs Overloading

OverridingOverloading
Same signatureDifferent parameters
Parent-child classesSame class
Runtime (dynamic)Compile-time (static)
PolymorphismFlexibility
Uses inheritanceNo inheritance needed
@Override annotationNo special annotation

Runtime Polymorphism

class Animal {
    void sound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }
}

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

public class Main {
    public static void main(String[] args) {
        Animal a;  // Parent reference

        a = new Dog();
        a.sound();  // Bark (Dog's method)

        a = new Cat();
        a.sound();  // Meow (Cat's method)
    }
}

Exception Rules

Cannot Throw New Checked Exceptions:

class Parent {
    void method() { }
}

class Child extends Parent {
    @Override
    void method() throws IOException { }  // ✗ Error
}

Can Throw Subclass Exception:

class Parent {
    void method() throws Exception { }
}

class Child extends Parent {
    @Override
    void method() throws IOException { }  // ✓ OK (IOException is subclass)
}

Quick Reference

class Parent {
    void method() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    @Override  // Recommended
    void method() {  // Same signature
        super.method();  // Optional: call parent
        System.out.println("Child");
    }
}

// Usage
Child c = new Child();
c.method();  // Calls Child's version

Parent p = new Child();
p.method();  // Also calls Child's version (polymorphism)

Exam Tips

Remember:

  1. Same signature required
  2. Use @Override annotation
  3. Cannot reduce access level
  4. Can increase access level
  5. Covariant return types allowed
  6. Use super to call parent method
  7. static methods not overridden (hidden)
  8. final methods cannot be overridden
  9. private methods not inherited (cannot override)
  10. Enables runtime polymorphism

Common Questions:

  • What is method overriding?
  • Rules for overriding?
  • Overriding vs overloading?
  • Can static methods be overridden?
  • What is @Override annotation?
  • Access modifier rules?
  • Return type rules?
  • How to call parent method?