9.4

Polymorphism

AP Computer Science A

The core idea

java
Animal a = new Dog("Rex");  // declared as Animal, actual object is Dog
a.speak();                   // calls Dog's speak(), not Animal's

Compile-time vs. runtime types

java
Animal a = new Dog("Rex");
//  ↑ compile-time type    ↑ runtime type

Example

java
Animal a = new Dog("Rex");
a.speak();    // OK — Animal has speak()
// a.fetch();  // COMPILE ERROR — Animal doesn't have fetch()

Polymorphism with arrays and ArrayLists

java
public class Animal {
    private String name;
    
    public Animal(String name) { this.name = name; }
    public String getName() { return name; }
    public String speak() { return name + " makes a sound"; }
}

public class Dog extends Animal {
    public Dog(String name) { super(name); }
    
    @Override
    public String speak() { return getName() + " barks"; }
}

public class Cat extends Animal {
    public Cat(String name) { super(name); }
    
    @Override
    public String speak() { return getName() + " meows"; }
}

public class Fish extends Animal {
    public Fish(String name) { super(name); }
    
    @Override
    public String speak() { return getName() + " blubs"; }
}
java
Animal[] pets = {
    new Dog("Rex"),
    new Cat("Whiskers"),
    new Fish("Nemo"),
    new Dog("Buddy")
};

for (Animal pet : pets) {
    System.out.println(pet.speak());
}
Rex barks
Whiskers meows
Nemo blubs
Buddy barks

With ArrayList

java
ArrayList<Animal> zoo = new ArrayList<>();
zoo.add(new Dog("Rex"));
zoo.add(new Cat("Whiskers"));
zoo.add(new Fish("Nemo"));

for (Animal a : zoo) {
    System.out.println(a.speak());
}

How Java resolves method calls

java
Animal a = new Dog("Rex");
a.speak();

Polymorphism with method parameters

java
public static void makeNoise(Animal a) {
    System.out.println(a.speak());
}

makeNoise(new Dog("Rex"));     // Rex barks
makeNoise(new Cat("Luna"));    // Luna meows
makeNoise(new Fish("Dory"));   // Dory blubs

The `instanceof` operator

java
Animal a = new Dog("Rex");

System.out.println(a instanceof Dog);    // true
System.out.println(a instanceof Animal); // true
System.out.println(a instanceof Cat);    // false

Casting

java
Animal a = new Dog("Rex");
// a.fetch();  // COMPILE ERROR — Animal doesn't have fetch()

Dog d = (Dog) a;  // downcast
d.fetch();         // OK — d is declared as Dog

Safe casting with `instanceof`

java
if (a instanceof Dog) {
    Dog d = (Dog) a;
    d.fetch();
}
java
Animal a = new Cat("Luna");
Dog d = (Dog) a;  // RUNTIME ERROR: ClassCastException!

Trace: Polymorphism in action

java
public class Shape {
    public String draw() { return "Drawing shape"; }
}

public class Circle extends Shape {
    public String draw() { return "Drawing circle"; }
}

public class Square extends Shape {
    public String draw() { return "Drawing square"; }
}
java
Shape s1 = new Circle();
Shape s2 = new Square();
Shape s3 = new Shape();

System.out.println(s1.draw());
System.out.println(s2.draw());
System.out.println(s3.draw());

Polymorphism does NOT apply to fields

java
public class Parent {
    public String name = "Parent";
}

public class Child extends Parent {
    public String name = "Child";
}

Parent p = new Child();
System.out.println(p.name);  // "Parent" — field, not method

Complete example: Payment system

java
public class Payment {
    private double amount;
    
    public Payment(double amount) {
        this.amount = amount;
    }
    
    public double getAmount() { return amount; }
    
    public String process() {
        return "Processing $" + String.format("%.2f", amount) + " payment";
    }
    
    public double fee() {
        return 0;  // no fee for base payment
    }
}

public class CreditPayment extends Payment {
    private String cardType;
    
    public CreditPayment(double amount, String cardType) {
        super(amount);
        this.cardType = cardType;
    }
    
    @Override
    public String process() {
        return super.process() + " via " + cardType;
    }
    
    @Override
    public double fee() {
        return getAmount() * 0.03;  // 3% processing fee
    }
}

public class CashPayment extends Payment {
    public CashPayment(double amount) {
        super(amount);
    }
    
    @Override
    public String process() {
        return super.process() + " in cash";
    }
}
java
ArrayList<Payment> payments = new ArrayList<>();
payments.add(new CreditPayment(100.00, "Visa"));
payments.add(new CashPayment(50.00));
payments.add(new CreditPayment(75.50, "Amex"));

double totalFees = 0;
for (Payment p : payments) {
    System.out.println(p.process());
    totalFees += p.fee();
}
System.out.println("Total fees: $" + String.format("%.2f", totalFees));
Processing $100.00 payment via Visa
Processing $50.00 payment in cash
Processing $75.50 payment via Amex
Total fees: $5.27

AP Exam Tips

Common Mistakes

Key Vocabulary