Skip to content

Lesson 02 · Inheritance & Polymorphism

Objectives

After this lesson you will be able to:

  • Use extends and super, and tell overriding from hiding.
  • Predict polymorphic dispatch — which method actually runs.
  • Override Object methods (equals, hashCode, toString) correctly.
  • Use final, casting, and instanceof safely.

extends and super

A subclass inherits accessible members and may add its own. A subclass constructor implicitly calls super() (the no-arg superclass constructor) as its first action unless you call super(...) or this(...) explicitly.

java
class Animal {
    Animal(String name) { }         // no no-arg constructor!
}
class Dog extends Animal {
    Dog() { super("dog"); }         // REQUIRED — Animal has no no-arg constructor
}

Exam trap

If the superclass has no no-arg constructor, the subclass must call super(...) explicitly, or it won't compile (the implicit super() has nothing to call).

Overriding vs hiding

Overriding replaces an inherited instance method; dispatch is dynamic (based on the object's runtime type). Hiding applies to static methods and fields; resolution is static (based on the reference's compile-time type).

java
class A { String who() { return "A"; }   static String s() { return "A.s"; } }
class B extends A {
    @Override String who() { return "B"; }   // overrides — dynamic
    static String s() { return "B.s"; }       // hides — static
}

A ref = new B();
ref.who();   // "B"   — instance method, dynamic dispatch
ref.s();     // "A.s" — static method, resolved by the reference type A

Exam trap

Fields are never polymorphic — a field access uses the reference type, not the object type. With class A{int x=1;} class B extends A{int x=2;}, ((A) new B()).x is 1.

An override cannot reduce visibility, cannot throw broader checked exceptions, and its return type must be the same or a covariant (narrower) subtype. Use @Override so the compiler catches mistakes.

Object methods

Every class extends Object. Override these together and consistently:

  • equals(Object) — logical equality. If you override it, override hashCode too, or hash-based collections break.
  • hashCode() — equal objects must return equal hash codes.
  • toString() — a readable representation.
java
@Override public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Point p)) return false;   // pattern: null-safe + cast in one
    return x == p.x && y == p.y;
}
@Override public int hashCode() { return Objects.hash(x, y); }

Gotcha

equals takes an Object parameter. Writing equals(Point p) overloads rather than overrides — collections still call the inherited Object.equals (identity). @Override would flag the mistake.

final, casting, instanceof

final on a class blocks subclassing; on a method blocks overriding. Downcasting needs a cast and is checked at runtime — a bad cast throws ClassCastException. Guard with instanceof (ideally the pattern form):

java
Object o = "hi";
if (o instanceof String s) { use(s.length()); }   // safe, no separate cast
String bad = (String) Integer.valueOf(1);          // ClassCastException at runtime

Key Takeaways

  • A subclass constructor runs super(...) first; if the parent lacks a no-arg constructor you must call super(args) explicitly.
  • Instance methods override (dynamic dispatch by object type); static methods and fields hide (resolved by reference type). Fields are never polymorphic.
  • Overrides can't narrow visibility or broaden checked exceptions; return types may be covariant. Use @Override.
  • Override equals/hashCode together; equals must take Object or it's just an overload.
  • Bad downcasts throw ClassCastException — guard with instanceof (pattern form casts for you).

Lesson Quiz

Lesson Quiz · Inheritance & Polymorphism0 / 5
  1. What does ref.who() and ref.s() print?

    class A { String who(){return "A";} static String s(){return "A";} }
    class B extends A { String who(){return "B";} static String s(){return "B";} }
    A ref = new B();
    • AB and B
    • BB and A
    • CA and A
    • DA and B
  2. What is ((A) new B()).x ?

    class A { int x = 1; }
    class B extends A { int x = 2; }
    • A1
    • B2
    • CCompile error
    • D0
  3. Why does this subclass not compile?

    class Animal { Animal(String n) {} }
    class Cat extends Animal { Cat() {} }
    • ACat needs @Override
    • BAnimal has no no-arg constructor, so implicit super() fails
    • CCat must be abstract
    • DIt compiles fine
  4. You override equals but not hashCode. What breaks?

    • ANothing
    • BHash-based collections (HashMap/HashSet) may not find equal objects
    • CCompile error
    • D== stops working
  5. Which signature actually OVERRIDES Object.equals?

    • Aboolean equals(Point p)
    • Bboolean equals(Object o)
    • Cint equals(Object o)
    • Dboolean Equals(Object o)

Next: Interfaces. Run the matching code in labs/src/main/java/com/jse21/m03_oop/.