dev only

Inheritance Hierarchies

Multiple Subclasses, One Superclass

An inheritance hierarchy forms when several subclasses inherit (directly, or through another subclass) from a common superclass. Intake, Shooter, and Climber all extending Subsystem is a small hierarchy; Object sits at the very top of every hierarchy in Java, since every class ultimately inherits from it (Lesson 17.4).

A Superclass Reference Can Hold Any Subclass Object

A variable typed as the superclass can refer to an object of that class or any of its subclasses:

Subsystem s1 = new Subsystem("Generic");
Subsystem s2 = new Intake("Front Intake");   // legal — an Intake IS-A Subsystem
Subsystem s3 = new Climber("Left Climber");  // legal — a Climber IS-A Subsystem

The reverse doesn't work — a subclass-typed variable cannot hold a plain superclass object, since not every Subsystem is specifically an Intake:

Intake i = new Subsystem("Generic"); // ERROR — a Subsystem isn't necessarily an Intake

Why This Matters: One Parameter Type, Every Subclass

A method, array, or collection typed against the superclass automatically accepts every subclass too — this is what makes managing a whole robot's subsystems from one place possible:

public void stopAll(Subsystem[] subsystems)
{
    for (Subsystem s : subsystems)
    {
        s.stop(); // works for an Intake, a Climber, a Shooter — anything extending Subsystem
    }
}

Subsystem[] robotSubsystems = { new Intake("Front"), new Climber("Left"), new Shooter("Main") };
stopAll(robotSubsystems);

The same holds for collections — an ArrayList<Subsystem> happily holds Intake, Climber, and Shooter objects side by side:

ArrayList<Subsystem> allSubsystems = new ArrayList<Subsystem>();
allSubsystems.add(new Intake("Front"));
allSubsystems.add(new Climber("Left"));

Which Method Actually Runs?

When a method typed against the superclass is called on an object that's actually a subclass instance, Java runs that object's actual version of the method — decided by its real runtime type, not the variable's declared type. This is the foundation of polymorphism, covered in full in Lesson 18.3 — for now, the key idea is just that a Subsystem-typed variable holding an Intake object still calls Intake's own overridden methods, if it has any.

Common Pitfalls

  • Trying to assign a superclass object to a subclass-typed variable. Intake i = new Subsystem(...); is a compile error — not every Subsystem is an Intake. This only ever works the other direction (superclass variable, subclass object).
  • Thinking a Subsystem[] array can only hold plain Subsystem objects. It can hold any mix of Subsystem and its subclasses — that flexibility is the entire point of typing the array against the superclass.
  • Forgetting which method version actually runs. Even though a variable is declared Subsystem, calling a method on it runs the object's real class's version — not necessarily Subsystem's own.

Key Takeaways

  • Several subclasses inheriting from one common superclass form an inheritance hierarchy, with Object at the top of every hierarchy in Java.
  • A superclass-typed variable, array, or collection can hold objects of that class or any of its subclasses — never the reverse.
  • This lets one method signature, one array type, or one collection type work seamlessly across an entire family of related subsystem classes.
  • Which method actually executes depends on the object's real runtime type, not the variable's declared type — the basis of polymorphism (Lesson 18.3).

Derived from csawesome-2026: unit-5-inheritance-optional/5.05-inheritance-hierarchies.md (CSA 5.5)