Java • Object-Oriented Programming

Method Overriding — Override methods and use @Override correctly

Learn how subclasses redefine inherited behavior and why the @Override annotation catches errors at compile time.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Imagine you inherit a family recipe, but you decide to swap in a new ingredient to improve it. The dish keeps the same name and purpose, but the details change. That is essentially what method overriding does in programming: a subclass provides its own version of a method it inherited from a parent class. This idea did not appear overnight — it grew alongside the concept of object-oriented programming itself.

1967 • Simula Introduces OOP

Simula, developed in Norway by Ole-Johan Dahl and Kristen Nygaard, introduced classes and subclasses. Subclasses could redefine inherited behavior, planting the seed for method overriding.

1980 • Smalltalk Popularizes Polymorphism

Smalltalk at Xerox PARC made message-passing and polymorphism central to programming. Every method call could be overridden by subclasses, making dynamic dispatch a core feature.

1995 • Java Launches with Built-in Override Support

James Gosling and Sun Microsystems released Java with virtual methods by default, meaning every non-static, non-final method could be overridden by subclasses.

2004 • Java 5 Adds @Override Annotation

Java 1.5 (Tiger) introduced the @Override annotation. This small addition let the compiler verify that a method truly overrides a parent method, catching typos and signature mismatches at compile time.

The central question is straightforward: when a subclass inherits a method from its parent, how do we safely and clearly replace that method's behavior? And how do we prevent subtle bugs — like misspelling a method name and accidentally creating a brand-new method instead of replacing the old one? Method overriding and the @Override annotation answer both questions.

SECTION 2

Core Principles & Definitions

Before you write a single line of override code, you need a clear mental model of the rules Java enforces. Method overriding is one face of polymorphism — the ability for objects of different classes to respond to the same method call in different ways. Here are the foundational ideas.

1

Same Signature Required

The overriding method in the subclass must have the exact same name, parameter list, and compatible return type as the method in the parent class.
2

IS-A Relationship

Overriding only happens between a superclass and its subclass (an IS-A relationship). You cannot override methods in unrelated classes.
3

Dynamic Dispatch at Runtime

Java determines which version of an overridden method to call based on the actual object type at runtime, not the reference type at compile time.
4

Access Cannot Be More Restrictive

If the parent method is public, the child's version must also be public. You can widen access (protected → public) but never narrow it.
5

@Override Is Your Safety Net

Adding @Override above a method tells the compiler: "I intend to override." If you mistype the method name or parameters, the compiler flags the error immediately.
✦

KEY TAKEAWAY

Think of method overriding like a franchise restaurant. The corporate headquarters (superclass) defines a recipe for a burger. Each local branch (subclass) can customize that burger — adding new toppings or swapping the bun — but it must still serve something called a "burger" with the same basic order process. The @Override annotation is like a quality inspector who checks that your local version actually matches the corporate menu item name — if you accidentally named it "brger," the inspector catches the mistake before a customer orders.

SECTION 3

Visual Explanation — Inheritance & Override Flow

Method Overriding — Class HierarchyAnimal (superclass)+ speak(): Stringreturn "..."+ move(): Stringreturn "moves"extendsDog (subclass)@Override+ speak(): Stringreturn "Woof!"move() inherited as-is// no override neededCat (subclass)@Override+ speak(): Stringreturn "Meow!"move() inherited as-is// no override needed= Overridden method body (new behavior)
Both Dog and Cat extend Animal. Each subclass overrides speak() with its own return value, while move() is inherited without change. The green and pink boxes highlight the overridden method bodies.

In the diagram above, the superclass Animal defines two methods: speak() and move(). Both subclasses — Dog and Cat — override speak() because each animal makes a different sound. However, neither subclass overrides move(). This means Dog and Cat both inherit the parent's default move() implementation. At runtime, when you call myAnimal.speak(), Java looks at the actual object's class — not the variable's declared type — and runs the correct version. This is dynamic dispatch in action.

SECTION 4

How Method Overriding Works Under the Hood

The Rules of Overriding

Java enforces a strict set of rules for method overriding. If any rule is violated, the compiler either rejects your code or treats the method as something other than an override. Understanding these rules prevents the majority of bugs students encounter.

  • ✓ Method name and parameter list must be identical. Changing even one parameter type creates an overloaded method, not an overridden one.
  • ✓ Return type must be the same or a subtype (called a covariant return type). For example, if the parent returns Object, the child may return String.
  • ✓ Access modifier cannot be more restrictive. A public method stays public; a protected method can become public but not private.
  • ✓ Static, final, and private methods cannot be overridden. Static methods belong to the class, not instances. Final methods are locked by design. Private methods are invisible to subclasses.
  • ✓ The super keyword lets the overriding method call the parent's original version inside the new method body, using super.methodName().

The @Override Annotation Explained

The @Override annotation is not required for overriding to work, but it is strongly recommended by every Java style guide. When you place @Override above a method, the compiler checks two things: (1) does a method with the exact same signature exist in the superclass or interface, and (2) is that method not final or static? If either check fails, the compiler raises an error. Without the annotation, a typo in your method name would silently create a new method that never gets called when you expect it to — a frustrating bug to track down.

⚠

Common Pitfall

If you write public boolean equals(MyClass other) instead of public boolean equals(Object other), you have accidentally overloaded — not overridden — the equals() method from Object. Adding @Override would have immediately flagged this mistake.

SECTION 5

Overriding vs. Overloading — A Common Confusion

Students frequently confuse method overriding with method overloading. They sound similar but operate in fundamentally different ways. Overriding replaces a parent's method in a subclass. Overloading creates multiple methods with the same name but different parameter lists within the same class or across a hierarchy.

Overriding vs. OverloadingMETHOD OVERRIDINGclass Parent {void greet() { ... }}↓ extendsclass Child extends Parent {@Overridevoid greet() { ... }}✔ Same name, same params✔ Different classes (parent→child)✔ Resolved at runtime✔ Enables polymorphism✔ @Override recommendedMETHOD OVERLOADINGclass Calculator {int add(int a, int b)double add(double a, double b)int add(int a, int b, int c)}✔ Same name, different params✔ Same class (or parent→child)✔ Resolved at compile time✔ Convenience / flexibility✘ @Override not applicableKey difference: same params (override) vs. different params (overload)
Left: method overriding — same method signature across parent and child classes, resolved at runtime. Right: method overloading — same method name but different parameter lists within one class, resolved at compile time.
FeatureOverridingOverloading
Method nameMust be identicalMust be identical
Parameter listMust be identicalMust differ
Class relationshipSuperclass ↔ subclassSame class or any class
Binding timeRuntime (dynamic)Compile time (static)
@Override annotationRecommendedNot applicable

Key differences between overriding and overloading

SECTION 6

Worked Example — Building an Override Step by Step

Let's walk through a complete example. We'll create a Shape superclass with a method area() that returns 0.0 as a default. Then we'll create a Circle subclass that overrides area() to compute the actual area of a circle.

Overriding area() in a Circle Subclass

  1. 1

    Step 1 — Define the Superclass

    We create a Shape class with a public method double area() that returns 0.0. This is the default implementation that any subclass will inherit.
    public class Shape { public double area() { return 0.0; } }
  2. 2

    Step 2 — Create the Subclass with extends

    We declare public class Circle extends Shape. This establishes the IS-A relationship: a Circle IS-A Shape. We also add a private field double radius and a constructor to initialize it.
    public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } }
  3. 3

    Step 3 — Write the Overriding Method with @Override

    Inside Circle, we add @Override on the line above our area() method. The method signature must match exactly: public double area(). Inside the body, we return Math.PI * radius * radius.
    @Override public double area() { return Math.PI * radius * radius; }
  4. 4

    Step 4 — Demonstrate Polymorphism

    In a main method, we declare a variable of type Shape but assign it a Circle object: Shape s = new Circle(5.0);. When we call s.area(), Java uses dynamic dispatch to run Circle's version.
    Output: 78.53981633974483 (which is π × 5²)
  5. 5

    Step 5 — Verify @Override Catches Mistakes

    If we had accidentally typed public double Area() (capital A) with the @Override annotation, the compiler would produce an error: "method does not override or implement a method from a supertype." Without the annotation, Java would silently accept the new method, and s.area() would still return 0.0 from Shape.
    @Override prevents silent bugs caused by typos or signature mismatches.

SECTION 7

Strengths, Limitations & Best Practices

StrengthsLimitations
Enables polymorphism — write code that works with the superclass type and seamlessly handles any subclass.Cannot override static, final, or private methods; these are off-limits by design.
@Override annotation gives compile-time safety, catching errors before your program ever runs.Overriding can create confusion when a deep hierarchy has many layers redefining the same method.
Supports the Open-Closed Principle: classes are open for extension but closed for modification.Overusing inheritance and overriding can lead to fragile base class problems, where changes to the parent break children.
The super keyword allows mixing parent behavior with new child behavior, promoting code reuse.Performance cost of dynamic dispatch is negligible but exists; JVM optimizes most cases with just-in-time compilation.

Strengths and limitations of method overriding

✦

BEST PRACTICE

Always use @Override whenever you intend to override a method. It costs nothing to write and saves hours of debugging. Think of it like wearing a seatbelt — you might not need it every drive, but when you do, you'll be glad it was there. Modern IDEs like IntelliJ and Eclipse can auto-insert @Override for you, so there's no excuse to skip it.

SECTION 8

Connection to Advanced OOP Concepts

Method overriding is your gateway to several powerful advanced concepts in Java. Once you are comfortable with basic overriding, you will encounter abstract methods (methods declared without a body that force subclasses to override them), interfaces (contracts that a class promises to implement), and design patterns like Template Method and Strategy that rely heavily on overriding.

ConceptWhat You Know NowWhat Comes Next
Regular overridingReplace a parent's concrete method with a new implementation in a subclass.Abstract methods: the parent declares the method with no body; the subclass must provide one.
@Override with classesUse @Override when extending a class.@Override with interfaces: the same annotation works when implementing interface methods (Java 6+).
Single inheritanceA class extends one superclass and overrides its methods.Multiple interfaces: a class can implement many interfaces, overriding default methods from each.
Calling superUse super.method() to invoke the parent version.Template Method pattern: the parent defines an algorithm skeleton and calls abstract methods that subclasses fill in.

Bridging from method overriding to advanced topics

As you progress in Java, you will find that virtually every framework — from Android development to web services — depends on method overriding. Mastering the basics now sets you up for success in AP Computer Science, college courses, and real-world software development.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

In your own words, explain the difference between method overriding and method overloading. Why is it important that overriding requires the exact same parameter list while overloading requires a different parameter list?
Show answer
Method overriding replaces an inherited method's behavior in a subclass, so the parameter list must match exactly — otherwise Java would not know which parent method you intend to replace. Method overloading provides multiple versions of a method with different inputs, resolved at compile time by the parameter types. If overriding allowed different parameters, the JVM could not perform dynamic dispatch because there would be ambiguity about which version to run. The identical signature is what allows Java to look at the actual object type at runtime and select the correct overridden method.

Question 2: BASIC

Given a class Vehicle with a method public String honk() { return "Beep!"; }, write a subclass Truck that overrides honk() to return "HOOOONK!" using the @Override annotation.
Show answer
The correct code is: public class Truck extends Vehicle { @Override public String honk() { return "HOOOONK!"; } }. The key elements are: (1) Truck extends Vehicle, establishing the IS-A relationship; (2) the @Override annotation signals our intent to the compiler; (3) the method signature — public String honk() — matches the parent exactly. If we accidentally wrote Honk() with a capital H, the @Override annotation would cause a compile error.

Question 3: INTERMEDIATE

A class Employee has a method public double calculatePay(int hoursWorked) { return hoursWorked * 15.0; }. A subclass Manager should override calculatePay so that managers earn double the base rate plus a $500 bonus. Write the Manager class and use super to call the parent's calculation.
Show answer
The solution is: public class Manager extends Employee { @Override public double calculatePay(int hoursWorked) { return super.calculatePay(hoursWorked) * 2 + 500.0; } }. By calling super.calculatePay(hoursWorked), we reuse the parent's logic (hoursWorked × 15.0) rather than duplicating it. We then multiply by 2 for double rate and add the 500 bonus. For 40 hours, a regular Employee earns $600.00, while a Manager earns $1,700.00. Using super promotes code reuse: if the base rate changes in Employee, Manager automatically reflects the update.

Question 4: APPLIED

You are building a game with a Character superclass that has public String attack() { return "punches for 5 damage"; }. Create two subclasses: Wizard (returns "casts fireball for 25 damage") and Archer (returns "shoots arrow for 15 damage"). Then write code in main that stores three characters in a Character[] array and loops through them, printing each one's attack. Explain which version of attack() runs for each element and why.
Show answer
Wizard class: public class Wizard extends Character { @Override public String attack() { return "casts fireball for 25 damage"; } }. Archer class: public class Archer extends Character { @Override public String attack() { return "shoots arrow for 15 damage"; } }. In main: Character[] party = { new Character(), new Wizard(), new Archer() }; for (Character c : party) { System.out.println(c.attack()); }. The output is: "punches for 5 damage", "casts fireball for 25 damage", "shoots arrow for 15 damage". Even though every element is declared as type Character, Java uses dynamic dispatch at runtime to call the actual object's overridden version. This is polymorphism in action — the same method call produces different behavior based on the object's real class.

Question 5: CRITICAL THINKING

Consider this code: a class Animal has public String toString() { return "I am an animal"; }. A subclass Dog has public String toString() { return "I am a dog"; }. Neither method uses @Override. First, explain which method is being overridden and from which class. Second, predict what happens if we call System.out.println(new Dog()). Third, explain why toString() is special in Java and why all classes can override it.
Show answer
Every class in Java implicitly extends java.lang.Object, which defines public String toString(). So Animal overrides Object's toString(), and Dog overrides Animal's toString(). When we call System.out.println(new Dog()), println internally calls toString() on the Dog object, and dynamic dispatch selects Dog's version, printing "I am a dog". Because Object is the root of every class hierarchy, toString() (along with equals() and hashCode()) can be overridden by any class. This is a real-world case where @Override should be used — adding it to both Animal's and Dog's toString() would confirm to the compiler that each truly overrides a parent method, protecting against signature typos.

SUMMARY

Summary

Summary

Method overriding allows a subclass to replace an inherited method with its own implementation, provided the method signature (name, parameter list, and compatible return type) matches exactly. This mechanism is the foundation of polymorphism — the ability to call the same method on different objects and get different behavior, determined at runtime via dynamic dispatch. The @Override annotation is your compile-time safety net: it tells the compiler to verify that your method truly overrides a parent method, catching typos and signature mismatches before your code ever runs.

Remember the key rules: you cannot override static, final, or private methods; the overriding method's access level cannot be more restrictive than the parent's; and super.methodName() lets you call the parent's original implementation from within the overridden method. Mastering these rules prepares you for abstract classes, interfaces, and the design patterns that power professional Java applications.

Varsity Tutors • Java • Method Overriding — Override methods and use @Override correctly