Polymorphism Lesson

Polymorphism: One Interface, Many Forms

Polymorphism, from the Greek for "many forms," is an OOP principle that allows you to treat objects of different classes as if they were objects of a common superclass. It's the key to writing flexible and extensible robot code.

The Core Concept

Polymorphism builds directly on inheritance and method overriding. It allows you to write code that works with a general type (like `Mechanism`) but have it automatically execute the specific, overridden behavior of a subtype (like `Intake` or `Shooter`).

Dynamic Method Dispatch

This is the magic behind polymorphism. When you call a method on an object, the Java Virtual Machine (JVM) checks the actual type of the object at runtime to decide which version of the method to run, not the type of the reference variable.

An FRC Example: A Universal `activate()` Method

Let's define a general `Mechanism` superclass with an `activate()` method. Then we'll create two specific subclasses, `Intake` and `Shooter`, that each provide their own unique implementation of `activate()`.

// 1. The Superclass
public class Mechanism {
    public void activate() {
        System.out.println("A generic mechanism is activating.");
    }
}

// 2. A Subclass
public class Intake extends Mechanism {
    @Override
    public void activate() {
        System.out.println("Intake rollers are spinning to pick up a note.");
    }
}

// 3. Another Subclass
public class Shooter extends Mechanism {
    @Override
    public void activate() {
        System.out.println("Shooter flywheel is spinning up to speed.");
    }
}
    

Polymorphism in Action

Now, we can create an array of `Mechanism` references but store different subclass objects in it. When we loop through and call `activate()` on each one, the JVM will dynamically dispatch the correct, overridden method.

// Create an array of the SUPERCLASS type
Mechanism[] robotMechanisms = new Mechanism[3];

// Store objects of the SUBCLASS types in the array (this is "upcasting")
robotMechanisms[0] = new Intake();
robotMechanisms[1] = new Shooter();
robotMechanisms[2] = new Intake();

// Loop through and call the same method on each object
for (Mechanism mech : robotMechanisms) {
    mech.activate(); // Polymorphism! The correct method is called at runtime.
}

// --- EXPECTED OUTPUT ---
// Intake rollers are spinning to pick up a note.
// Shooter flywheel is spinning up to speed.
// Intake rollers are spinning to pick up a note.
    

Why is This So Powerful?

This approach allows you to write incredibly flexible code. You can write a method that accepts a generic `Mechanism` object, and that same method will work correctly with an `Intake`, a `Shooter`, or any other `Mechanism` subclass you create in the future, without needing any changes.

Test Your Knowledge

Question: Given the code `Mechanism myMechanism = new Shooter();`, which `activate()` method will be called when `myMechanism.activate()` is executed?