Skip to content

Overriding a method: the four checks

☕ Java 17 · Rewriting What Your Parent Class Does · 1/3

Three choices for an inherited method

Choice How Result
Keep it write nothing the child uses the parent's version
Replace it declare the same method in the child overriding: the child's version runs
Lock it the parent marks it final no child can replace it

Shared code is written once in the parent; each child rewrites only what differs.

Replacing a parent method

class Subscription {
    public int getPrice() {
        return 10;
    }
}

class PremiumPlan extends Subscription {
    public int getPrice() {
        return super.getPrice() + 8;
    }
}

new Subscription().getPrice() returns 10; new PremiumPlan().getPrice() returns 18.

super.getPrice() calls the parent's version. Forget super and write getPrice() + 8: the method calls itself forever, and the program crashes with a StackOverflowError.

The real object decides which version runs, even through a parent-typed variable: Subscription s = new PremiumPlan(); s.getPrice() returns 18.

The four checks

Every override must pass four compiler checks:

# Check Example
1 Same signature: name + parameter types play() vs play(int speed) is an overload, not an override
2 Same access or more open protected → protected or public, never package or private
3 No new or broader checked exception throws FileNotFoundException → the same, a narrower one, or none
4 Same return type, or a subtype CharSequence getTitle() → String getTitle() is fine

The logic: code that uses the parent type must be able to trust the parent's promises. A child may give more, never less.

Access and exceptions in practice

class Content {
    public void play() { }
    void load() throws FileNotFoundException { }
}

class Movie extends Content {
    protected void play() { }               // ❌ narrower than public
    void load() throws IOException { }      // ❌ broader checked exception
}

The fixed version keeps public and drops the exception:

class Movie extends Content {
    public void play() { }                  // ✅
    void load() { }                         // ✅ throwing nothing is allowed
}

An override may always add unchecked exceptions (like IllegalStateException): check 3 only concerns checked ones.

Return types can get more specific

class Content {
    public CharSequence getTitle() { return "Dune"; }
    public Number getYear() { return 2021; }
}

class Movie extends Content {
    public String getTitle() { return "Dune"; }   // ✅ String is a CharSequence
    public Integer getYear() { return 2021; }     // ✅ Integer is a Number
}

This is a covariant return type. The reverse fails: if the parent returns String, the child can't return CharSequence. Quick test: does the child's value fit the parent's return type without a cast? And void only matches void.

These checks apply to instance methods. Static methods, fields and private methods follow different rules.

💻 Code: Java17_Part_6_Object_Oriented_Basics on GitHub

Next: @Override, hiding and final →