THINK FIRST·CODE LATER

← Java Programming
Chapter 14 · Week 8–10

Inheritance, Composition, and Aggregation

Before You Start: What You Must Be Able to Do

Before the questions, make sure you can: distinguish an is-a relationship from a has-a relationship and choose inheritance or composition accordingly; write a subclass with extends and say exactly which members it inherits; explain what super() does, when the compiler inserts it, and what happens if the superclass has no no-argument constructor; give the order in which fields, initialiser blocks and constructors run; distinguish overriding from overloading and state the rules an overriding method must obey; and say what final does to a field, a method and a class.

Is-a versus has-a

  • Inheritance (is-a). A SavingsAccount is a BankAccount. Write class SavingsAccount extends BankAccount.
  • Composition (has-a, strong ownership). A Car has an Engine, and the engine has no independent life: the Car creates it and it disappears with the car.
  • Aggregation (has-a, weak ownership). A Course has Student objects, but the students exist before and after the course; the course merely holds references to them.

Prefer composition when in doubt: if you cannot say “every X is a Y” out loud without wincing, do not use extends.

Writing a subclass

public class Vehicle {
    protected String brand;                 // visible to subclasses
    private int serial;                     // NOT accessible in the subclass
    public Vehicle(String brand) { this.brand = brand; }
    public void describe() { System.out.println("Vehicle: " + brand); }
}

public class Car extends Vehicle {
    private int doors;
    public Car(String brand, int doors) {
        super(brand);                       // must be the FIRST statement
        this.doors = doors;
    }
    @Override
    public void describe() {                // overriding
        super.describe();                   // optional call to the inherited version
        System.out.println("with " + doors + " doors");
    }
}

A subclass inherits all public and protected members (and package-private ones in the same package). It does not inherit constructors, and it cannot directly touch private fields of the superclass — it must go through inherited accessors and mutators.

private the declaring class only
(none) any class in the same package
protected same package, plus subclasses anywhere
public everywhere

Java supports single inheritance of classes: one extends only. Every class that does not name a superclass extends Object, which is why every object has toString, equals and hashCode.

Constructors, super(), and initialisation order

  • A constructor begins with this(...) or super(...). If you write neither, the compiler inserts super() — the no-argument superclass constructor.
  • Therefore, if the superclass declares only constructors that take arguments, every subclass constructor must call super(...) explicitly. If it does not, the class does not compile.
  • Order of execution when new Child() runs: the superclass constructor chain finishes first (up to Object), then the subclass's field initialisers and instance initialiser blocks in the order they appear in the source, then the body of the subclass constructor.
class A { A() { System.out.print("1"); } }
class B extends A {
    int x = print("2");
    { System.out.print("3"); }                  // instance initialiser block
    B()  { System.out.print("4"); }
    static int print(String s) { System.out.print(s); return 0; }
}
// new B() prints 1234

Overriding versus overloading

Overloading Overriding
Where same class (or inherited) subclass redefines a superclass method
Signature must differ must be identical (name and parameter list)
Return type irrelevant same, or a subtype (covariant)
Access free may not be more restrictive than the original
Bound at compile time (static binding) at run time (dynamic binding)

An overriding method may not throw broader checked exceptions, and static, final and private methods cannot be overridden — a static method redeclared in a subclass is hidden, not overridden. The @Override annotation is optional but valuable: it turns a silent mistake (a misspelled name, a wrong parameter type, which would quietly become an overload) into a compile-time error.

final

final on a variable means assign once; on a method, it cannot be overridden; on a class, it cannot be extended (String is final). A final instance field must be definitely assigned by the end of every constructor — it may be given its value in the declaration or in the constructor, but not both, and never afterwards.

Composition in code

public class Car {                       // composition: the Car owns its Engine
    private final Engine engine = new Engine(1600);
    public void start() { engine.ignite(); }      // delegation
}
public class Course {                    // aggregation: students exist independently
    private ArrayList<Student> enrolled = new ArrayList<>();
    public void enrol(Student s) { enrolled.add(s); }
}

Composition reuses code by delegating to a contained object rather than inheriting from a superclass; it is more flexible, because the contained object can be replaced at run time.

Remember
  • extends means is-a; a field means has-a.
  • Constructors are not inherited; super(...) must come first.
  • No no-argument superclass constructor ⇒ the subclass must call super(...) explicitly.
  • Superclass constructor runs before the subclass body.
  • Same signature = overriding; different signature = overloading.
  • private members are not accessible in a subclass.
Common Pitfalls
  • Using extends for a has-a relationship (class Car extends Engine).
  • Forgetting super(...) when the superclass has no no-argument constructor.
  • Believing that redeclaring a field in a subclass overrides it — fields are hidden, not overridden.
  • Changing the parameter list while “overriding” and creating an accidental overload.

Ready? Close the notes and practise.

37 questions. Predict the output before you check — that is the skill the exam measures.