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 exact signature: same name, same parameter list, and the same (or a subtype of the) return type.
public abstract class Subsystem
{
public void stop() { System.out.println("Stopping subsystem"); }
}
public class Intake extends Subsystem
{
@Override
public void stop()
{
motor.set(0); // Intake's own, specific version replaces Subsystem's
}
}
@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 exact same signature, in a subclass, replacing the superclass's version. Overloading means the same method name, in the same class, with a different parameter list:
public class Subsystem
{
public void log(String message) { System.out.println(message); }
public void log(String message, int code) { System.out.println(message + " [" + code + "]"); } // overload — different parameters
}
Two methods can never differ only in return type — that alone doesn't count as a valid overload, and doesn't count as an override either (a mismatched return type on an otherwise-identical signature is just 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:
public class Subsystem
{
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
public class Intake extends Subsystem
{
public void rename(String newName)
{
setName(newName); // must go through the inherited setter — direct access to `name` isn't legal here
}
}
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. - Trying to differ only by return type.
public int stop()next to a superclass'spublic void stop()doesn't compile — that's neither a valid override nor a valid overload.
Key Takeaways
- Overriding replaces an inherited method with a new version in the subclass, using the exact same signature (name, parameters, return type).
@Overrideisn'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, same class, different parameter list — 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.
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")