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
- •
- •
- •
- •
- •
- •