Quiz 2

Inheritance — extends, super, Overriding, Constructor Chaining

2361 words
12 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# Inheritance — extends, super, Overriding, Constructor Chaining ## 🎯 Learning Objectives By the end of this topic, you will be able to: - Create class hierarchies using `extends` - Use `super` to access parent constructors and methods - Override methods correctly with `@Override` - Understand constructor chaining...

Inheritance — extends, super, Overriding, Constructor Chaining

🎯 Learning Objectives

By the end of this topic, you will be able to:
  • Create class hierarchies using extends
  • Use super to access parent constructors and methods
  • Override methods correctly with @Override
  • Understand constructor chaining and initialization order
  • Use final to prevent inheritance and overriding
  • Distinguish inheritance from composition

📋 Prerequisites


1. Intuition: What Problem Does Inheritance Solve?

1.1 The Problem

Imagine modeling animals in a zoo. Without inheritance:
java
public class Dog {
    String name; int age;
    void eat() { /* ... */ }
    void sleep() { /* ... */ }
    void bark() { System.out.println("Woof!"); }
}
public class Cat {
    String name; int age;  // DUPLICATE!
    void eat() { /* ... */ }  // DUPLICATE!
    void sleep() { /* ... */ }  // DUPLICATE!
    void meow() { System.out.println("Meow!"); }
}
Everything is duplicated. Inheritance lets you share common code through a hierarchy:
java
public class Animal {
    String name; int age;
    void eat() { /* ... */ }
    void sleep() { /* ... */ }
}
public class Dog extends Animal {
    void bark() { System.out.println("Woof!"); }
}
public class Cat extends Animal {
    void meow() { System.out.println("Meow!"); }
}

1.2 Mental Model: Family Tree

java
        Animal (base class)
       /      \
      Dog     Cat (derived classes)
     /   \
  Beagle Poodle (further derived)
Each child inherits everything from ancestors and can add or modify behavior. A Beagle IS-A Dog, which IS-A Animal.

2. The extends Keyword

2.1 Basic Syntax

java
public class Parent {
    // fields, methods
}
public class Child extends Parent {
    // inherits Parent's public/protected members
    // can add new members
    // can override inherited methods
}

2.2 What Gets Inherited?

MemberSubclass Access
publicInherited and accessible
protectedInherited and accessible
package-private (default)Inherited only if subclass is in same package
privateNOT inherited (exists in object but not accessible)
staticInherited (shared, can be hidden)
ConstructorsNOT inherited (must be called via super)

2.3 Single Inheritance

Java supports single inheritance for classes — a class can extend only ONE parent class:
java
public class A extends B { }  // OK
public class C extends B, D { }  // Compile error! No multiple inheritance
Multiple inheritance is achieved through interfaces (covered separately).

3. The super Keyword

3.1 Calling Parent Constructor

The first statement of any constructor must be a call to a parent constructor:
java
public class Vehicle {
    private String brand;
    public Vehicle(String brand) {
        this.brand = brand;
    }
}
public class Car extends Vehicle {
    private int doors;
    public Car(String brand, int doors) {
        super(brand);  // Must be first statement
        this.doors = doors;
    }
}
If you don't write super(), Java inserts an implicit super() (no-arg):
java
public class Parent {
    public Parent() { System.out.println("Parent created"); }
}
public class Child extends Parent {
    public Child() {
        // Implicit: super(); inserted by compiler
        System.out.println("Child created");
    }
}
// new Child() prints:
// "Parent created"
// "Child created"
Error: If parent has NO no-arg constructor, you MUST explicitly call super(args).

3.2 Calling Parent Method

java
public class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}
public class Dog extends Animal {
    @Override
    public void eat() {
        super.eat();  // Call parent's version
        System.out.println("Dog is eating dog food");
    }
}

4. Method Overriding

4.1 Rules for Overriding

java
public class Parent {
    public void display() { System.out.println("Parent"); }
    protected void show() { System.out.println("Parent show"); }
    public final void cannotOverride() { }
}
public class Child extends Parent {
    @Override
    public void display() { System.out.println("Child"); }  // OK
    @Override
    protected void show() { System.out.println("Child show"); }  // OK
    // @Override
    // public void cannotOverride() { }  // Compile error! Final method
}
Override rules:
  1. Same method signature (name + parameter types)
  2. Return type must be same or covariant (subtype)
  3. Access level must be same or wider (protected → public OK, public → protected not OK)
  4. Can't override final, static, or private methods

4.2 @Override Annotation

Always use @Override — it causes a compile error if you mis-spell the method name or get the signature wrong, saving you from runtime bugs.
java
public class Parent {
    public void doSomething(int x) { }
}
public class Child extends Parent {
    @Override
    public void doSomething(int x) { }  // OK — actually overrides
    // @Override
    // public void doSomething(double x) { }  // Overloading, not overriding!
}

4.3 Covariant Return Types

java
public class Parent {
    public Parent getInstance() { return new Parent(); }
}
public class Child extends Parent {
    @Override
    public Child getInstance() { return new Child(); }  // OK: Child is subtype of Parent
}

4.4 Access Level Rules

(Diagram)

5. Constructor Chaining — Detailed Trace

java
class Grandparent {
    Grandparent() { System.out.println("1: Grandparent"); }
}
class Parent extends Grandparent {
    Parent() { System.out.println("2: Parent"); }
}
class Child extends Parent {
    Child() { System.out.println("3: Child"); }
}
public class Test {
    public static void main(String[] args) {
        Child c = new Child();
    }
}
// Output:
// 1: Grandparent
// 2: Parent
// 3: Child
Chain of calls:
  1. new Child() calls Child()
  2. Child() implicitly calls super()Parent()
  3. Parent() implicitly calls super()Grandparent()
  4. Grandparent() executes body (prints "1: Grandparent")
  5. Returns to Parent(), executes body (prints "2: Parent")
  6. Returns to Child(), executes body (prints "3: Child") This is the bottom-up then top-down execution: constructors chain up, then execute body top-down.

6. The final Keyword

6.1 Final Class (Cannot Be Extended)

java
public final class String { }  // You cannot extends String
public final class Math { }    // Cannot be extended

6.2 Final Method (Cannot Be Overridden)

java
public class Parent {
    public final void securityCheck() {
        // This implementation must never change
    }
}

6.3 Final Variable (Constant)

java
public final int MAX_SIZE = 100;  // Cannot be reassigned

7. Inheritance vs Composition

Both model "is-a" (inheritance) and "has-a" (composition) relationships:
java
// Inheritance: Car IS-A Vehicle
public class Car extends Vehicle { }
// Composition: Car HAS-A Engine
public class Car {
    private Engine engine;  // Composition
}
Prefer composition over inheritance when:
  • The relationship is "has-a", not "is-a"
  • You want to reuse behavior, not type identity
  • The base class is not designed for extension
java
// Instead of: class Stack extends ArrayList (BAD!)
// Use composition:
public class Stack<E> {
    private ArrayList<E> list = new ArrayList<>();
    public void push(E item) { list.add(item); }
    public E pop() { return list.remove(list.size() - 1); }
}

8. Java vs Python: Inheritance

FeatureJavaPython
Syntaxclass Child extends Parentclass Child(Parent):
Multiple inheritanceClasses: No. Interfaces: YesYes (with MRO)
super()super(args) (call parent constructor)super().__init__(args)
@OverrideExplicit annotation recommendedNot needed (duck typing)
final classCannot be extendedNo equivalent (convention only)
final methodCannot be overriddenNo equivalent
Access controlModifiers control inheritanceAll methods are virtual
Abstract enforcementCompile-timeRuntime

9. Common Pitfalls

Pitfall 1: Overriding Instead of Overloading

java
class Parent {
    void show(int x) { }
}
class Child extends Parent {
    void show(double x) { }  // Overloading, not overriding!
}
Why: Different parameter type. The child has BOTH methods. When called with an int, the parent's version runs. Fix: Use @Override to catch signature mismatches.

Pitfall 2: Calling Overridable Method from Constructor

java
class Parent {
    Parent() { init(); }
    void init() { }
}
class Child extends Parent {
    String name = "Child";
    @Override void init() { System.out.println(name); }
}
// new Child() prints "null", not "Child"!
Why: When parent constructor runs, child's name hasn't been initialized yet. Fix: Never call overridable methods from constructors. Make such methods private or final.

Pitfall 3: Forgetting super() When Parent Has No No-Arg Constructor

java
class Parent {
    Parent(int x) { }
}
class Child extends Parent {
    Child() { }  // Error: implicit super() doesn't exist!
}
Why: Parent only has a parameterized constructor. Java can't insert super(). Fix: Child() { super(0); }

Pitfall 4: Downcasting Without instanceof Check

java
Animal a = new Dog();
Cat c = (Cat) a;  // ClassCastException at runtime
Why: a is a Dog, not a Cat. The cast is invalid. Fix: if (a instanceof Cat) { Cat c = (Cat) a; }

Pitfall 5: Using Inheritance for Code Reuse Only

java
class Dog extends ArrayList<String> { }  // BAD! Dog IS-NOT-A list
Why: Inheritance should model "is-a", not just code reuse. If Dog just needs a collection, use composition. Fix: class Dog { private List<String> tricks; }

10. Practice Questions

Q1: What is the output?
java
class Parent {
    Parent() { System.out.print("A "); }
}
class Child extends Parent {
    Child() { System.out.print("B "); }
}
class GrandChild extends Child {
    GrandChild() { System.out.print("C "); }
}
public class Test {
    public static void main(String[] args) {
        new GrandChild();
    }
}
Answer: A B C
Constructor chaining: GrandChild calls Child (via super), Child calls Parent (via super). Parent executes ("A"), returns to Child ("B"), returns to GrandChild ("C"). Q2: Does this code compile? Explain.
java
class Parent {
    private void secret() { }
}
class Child extends Parent {
    private void secret() { }
}
Answer: Yes, it compiles. secret() in Parent is private, so Child doesn't inherit it. Child's secret() is a completely new method, not an override. There's no @Override annotation, so no conflict. Q3: What is wrong with this code?
java
class Parent {
    Parent(int x) { }
}
class Child extends Parent {
    int y;
    Child(int x, int y) {
        this.y = y;
        super(x);  // super() must be FIRST statement!
    }
}
Answer: super(x) must be the first statement in the constructor, but this.y = y comes before it. Fix: move super(x) to the first line. Q4: What is the output?
java
class A {
    void show() { System.out.println("A"); }
}
class B extends A {
    void show() { System.out.println("B"); }
}
class C extends B {
    void show() { System.out.println("C"); }
}
public class Test {
    public static void main(String[] args) {
        A obj = new C();
        obj.show();
    }
}
Answer: C
Dynamic dispatch: Reference is A, but actual object is C. JVM calls C's show() at runtime. Q5: Can you override a static method? Explain.
Answer: No. Static methods are hidden, not overridden. The method called depends on the reference type at compile-time:
java
class Parent {
    static void greet() { System.out.println("Parent"); }
}
class Child extends Parent {
    static void greet() { System.out.println("Child"); }
}
Parent p = new Child();
p.greet();  // Prints "Parent" (compile-time binding, not dynamic dispatch)
Q6: What is the difference between super and this?
Answer:
  • this: Reference to the current object. Used to access current class members, pass current object, or call another constructor in same class (this(...)).
  • super: Reference to the parent class. Used to access parent members hidden by overridden methods, or call parent constructor (super(...)).
  • Both must be the first statement if used as constructor calls. Q7: What is a covariant return type?
Answer: An overriding method can return a subtype of the original method's return type:
java
class Parent {
    Number getValue() { return 0; }
}
class Child extends Parent {
    @Override
    Integer getValue() { return 42; }  // Integer IS-A Number
}
This is covariant return — the return type can "vary together" (co-vary) with the subclass. Q8: Why does Java not support multiple inheritance of classes?
Answer: Java avoids multiple class inheritance to prevent the diamond problem:
java
class A { void foo() { } }
class B extends A { void foo() { } }
class C extends A { void foo() { } }
class D extends B, C { }  // Which foo() does D inherit?
Java solves this by allowing multiple interfaces (which have no state and default methods resolve conflicts explicitly). Q9: What does the final keyword do when applied to a class?
Answer: final class prevents the class from being subclassed (extended). Common examples: String, Integer, Math, System. This is done for:
  1. Security: prevent malicious subclassing
  2. Immutability: prevents mutable subclasses
  3. Optimization: compiler can inline methods more aggressively Q10: When should you prefer composition over inheritance?
Answer: Prefer composition when:
  1. The relationship is "has-a" not "is-a" (e.g., Car has-a Engine)
  2. You only need code reuse, not polymorphic behavior
  3. The base class is fragile (changes break subclasses)
  4. You need multiple "parents" (composition allows multiple objects)
Effective Java (Item 18) recommends: "Favor composition over inheritance" because inheritance breaks encapsulation when the base class changes.

📐 Key Concepts

ConceptSyntaxPurpose
Extendsclass B extends ADeclare inheritance
Super constructorsuper(args)Call parent constructor
Super methodsuper.method()Call overridden parent method
Override@OverrideReplace parent method implementation
Final classfinal class APrevent subclassing
Final methodfinal void m()Prevent overriding
Downcasting(Child) parentRefCast to subtype (risky)
UpcastingImplicitAssign child to parent ref (safe)

🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.