Overriding Methods

What Overriding Is

A subclass inherits all its superclass's public methods unchanged — but it can also override one, replacing the inherited version with its own. Overriding requires matching the superclass method's signature — the same name and the same parameter list — with a return type that is the same as the superclass method's, or a subtype of it (called a covariant return type).

RobotPart (Lesson 17.1) has a general-purpose stop() that only logs a message: public void stop() { log("stopped"); }. Intake overrides it with a version specific to its own rollers:

public class Intake extends RobotPart
{
    public Intake(String name) { super(name); }

    @Override
    public void stop()
    {
        // Intake's own, specific version replaces RobotPart's
        log("rollers off, game piece held");
    }
}

@Override is optional but always worth writing — it tells the compiler "I intend to override an inherited method here," and the compiler then checks the signature truly matches. Without it, a typo in the method name (stopp() instead of stop()) would silently create a brand-new, unrelated method instead of overriding anything — with @Override, that same typo becomes a compile error instead of a bug that only shows up at runtime.

Overriding vs. Overloading

These sound similar but are entirely different things. Overriding means the same signature, in a subclass, replacing the superclass's version. Overloading means the same method name with a different parameter list — either in the same class, or added by a subclass alongside the method it inherited. Here RobotPart overloads its own log():

public class RobotPart
{
    // ...name field, constructor, getName(), and stop() as in Lesson 17.1
    public void log(String msg) { System.out.println("[" + name + "] " + msg); }

    // overload — same name, different parameters
    public void log(String msg, int code) { log(msg + " (code " + code + ")"); }
}

Two methods can never differ only in return type — that alone doesn't count as a valid overload. And a subclass method with the same name and parameters as an inherited one, but an incompatible return type (for example int where the superclass method returns void), isn't an override either: it's a compile error.

Inherited Getters and Setters Still Apply

A subclass gets its superclass's public getters and setters automatically, and — since the superclass's fields are private — those accessor methods remain the only way to reach them, even from the subclass itself (unless the superclass marks a field protected, Lesson 17.2):

public class Intake extends RobotPart
{
    // ...constructor and stop() as above

    public void announce()
    {
        // must go through the inherited getter — direct access to name isn't legal here
        System.out.println("Intake online: " + getName());
    }
}

Preventing an Override: final

A method declared final can't be overridden: if RobotPart declared public final String getName(), any subclass that tried to write its own getName() would fail to compile. A whole class can be final too, which means it can't be extended at all — String is one, so class MyString extends String doesn't compile. Lesson 23.1 covers final in full.

Common Pitfalls

  • Mismatching the signature and expecting an override anyway. Changing the parameter list (even by adding one parameter) makes it an overload, not an override — the original method is still inherited unchanged, sitting alongside the new one.
  • Forgetting @Override. Skipping it doesn't break anything when the signature is right, but it removes the compiler's safety net against typos — always include it when overriding.
  • Changing only the return type. public int stop() next to a superclass's public void stop() doesn't compile — int isn't compatible with void, so it's neither a valid override nor a valid overload.

Key Takeaways

  • Overriding replaces an inherited method with a new version in the subclass: same name and parameter list, with a return type that's the same or a subtype (covariant).
  • @Override isn't required, but should always be used — it turns a signature typo into a compile error instead of a silent, unrelated new method.
  • Overloading is different: same name, different parameter list — in the same class, or added in a subclass — not to be confused with overriding.
  • Two methods can never differ only in return type.
  • Inherited getters/setters remain the only way to reach a superclass's private fields, even from within the subclass.
  • A final method can't be overridden, and a final class (like String) can't be extended.

Derived from csawesome-2026: unit-5-inheritance-optional/5.03-overriding-methods.md (CSA 5.3) Deck context: mechacoder-test/src/lessons/java-2.js, slide 5 ("Polymorphism: Many Forms")