🧬 Inheritance & Polymorphism · Intermediate

@Override in Java

Lets the compiler catch accidental overloads and typos.

🧩 The mysteryYou override toString(), run the program — and still get Cat@1b6d3586. Your method is right there! Look closer: tostring. One annotation would have caught it.

A promise to the compiler

@Override tells the compiler: "this method must override something." If it doesn't, compilation fails. It's optional — overriding works without it — but it turns silent mistakes into loud errors.

⚠️ The trap

The silent typo

Without @Override, tostring() (lowercase s) compiles happily as a brand-new method, and println keeps using the default toString(). With the annotation, the compiler rejects it on the spot.

class Cat {
    @Override  // error: overrides nothing
    public String tostring() {
        return "cat";
    }
}
🔮 Predict it

The equals mystery

Pt defines equals(Pt). What prints?

class Pt {
    int x; Pt(int x) { this.x = x; }
    boolean equals(Pt o) { return x == o.x; }
}
void main() {
    Object a = new Pt(5), b = new Pt(5);
    System.out.println(a.equals(b));
    Pt c = new Pt(5), d = new Pt(5);
    System.out.println(c.equals(d));
}
  1. true true
  2. false true
  3. false false
Show the answer

false, then true! Same objects, different answers — depending only on the declared types. Next screen explains why.

Overload, not override

equals(Pt) does not override Object.equals(Object) — the parameter type differs, so it's an overload. Through Object references the compiler can only pick equals(Object): the inherited identity check. Through Pt references it picks equals(Pt).

Writing equals

✗ Accidental overload
public boolean equals(Pt other) {
    return x == other.x;
}

Wrong parameter type. Adding @Override would flag it.

✓ Real override
@Override
public boolean equals(Object o) {
    return o instanceof Pt p
        && x == p.x;
}

Takes Object, checks the type, compares fields.

Where @Override works

On methods that override a superclass method or (since Java 6) implement an interface method. Not on fields, and not on methods that override nothing.

💼 In the real world

In real projects

Style guides such as Google's Java Style Guide require @Override wherever it's legal, and IDEs add it automatically. It's free insurance: refactor a parent's method signature and every stale override lights up red.

Key takeaways

  1. Optional, but turns silent mistakes into compile errors
  2. Catches typos like tostring()
  3. Catches accidental overloads like equals(Point)
  4. Works for superclass and interface methods
🤯 Did you know?

@Override has *source* retention: it disappears after compilation and leaves no trace in the .class file. Its whole job happens inside the compiler.

Practice questions

What does this print?

class Cat {
    @Override
    public String tostring() {
        return "cat";
    }
}
void main() { System.out.println(new Cat()); }
  1. cat
  2. Cat@1b6d3586
  3. Compile error
Check your answer

Compile error. tostring (lowercase s) overrides nothing, so @Override makes the compiler reject it. Without the annotation the typo would compile and the default toString would run.

What does this print?

class Pt {
    int x; Pt(int x) { this.x = x; }
    public boolean equals(Pt o) {
        return x == o.x;
    }
}
void main() {
    Object a = new Pt(1), b = new Pt(1);
    System.out.println(a.equals(b));
}
  1. true
  2. false
  3. Compile error
Check your answer

false. equals(Pt) overloads instead of overriding equals(Object). Through Object references the compiler picks equals(Object), the inherited identity check.

Next: one variable, many possible objects — how does Java choose which method runs?