09. Polymorphism
Polymorphism means a **parent reference can point to a child object**, and the method actually called is decided at runtime. Together with `abstract` classes, it's the backbone of flexible OOP design.
What you'll learn
- 1Understand polymorphism via parent reference / child object
- 2Declare an `abstract` class and abstract methods
- 3Use `instanceof` and JDK 16+ pattern matching
- 4Choose between an abstract class and an interface (interface in lecture 10)
Overview
Polymorphism means a **parent reference can point to a child object**, and the method actually called is decided at runtime. Together with `abstract` classes, it's the backbone of flexible OOP design.
Core Concepts
1) Parent reference / child object
Animal a = new Dog("Rex"); // upcasting
a.speak(); // calls Dog.speak() (dynamic dispatch)Even though the reference type is `Animal`, the actual object is `Dog` so the override wins.
2) `abstract` class
abstract class Shape {
abstract double area(); // no body β must be implemented by subclass
void print() { System.out.println("area=" + area()); }
}- Has at least one abstract method
- Cannot be instantiated with `new`
- Subclasses **must** implement every abstract method
3) `instanceof` and pattern matching
Object o = "hello";
if (o instanceof String s) {
System.out.println(s.toUpperCase()); // JDK 16+ pattern matching
}4) Downcasting
Animal a = new Dog("Rex");
Dog d = (Dog) a; // safe because the actual object is Dog
// Cat c = (Cat) a; // ClassCastException at runtimeAlways check with `instanceof` before downcasting.
Examples
Example 1 β `ShapeDemo.java`: abstract class polymorphism
public class ShapeDemo {
static abstract class Shape {
abstract double area();
}
static class Rectangle extends Shape {
double w, h;
Rectangle(double w, double h) { this.w = w; this.h = h; }
@Override double area() { return w * h; }
}
static class Circle extends Shape {
double r;
Circle(double r) { this.r = r; }
@Override double area() { return Math.PI * r * r; }
}
public static void main(String[] args) {
Shape[] shapes = { new Rectangle(3, 4), new Circle(2) };
for (Shape s : shapes) System.out.printf("%.3f%n", s.area());
}
}**Output**
12.000
12.566**Note:** the same `s.area()` call dispatches to different implementations at runtime.
Example 2 β `Speak.java`: dynamic dispatch
public class Speak {
static class Animal { void speak() { System.out.println("..."); } }
static class Dog extends Animal { @Override void speak() { System.out.println("Woof"); } }
static class Cat extends Animal { @Override void speak() { System.out.println("Meow"); } }
public static void main(String[] args) {
Animal[] zoo = { new Dog(), new Cat(), new Animal() };
for (Animal a : zoo) a.speak();
}
}**Output**
Woof
Meow
...**Note:** the array element type is `Animal`, but each call resolves to the actual subtype's method.
Example 3 β `PatternMatch.java`: `instanceof` pattern matching
public class PatternMatch {
static String describe(Object o) {
if (o instanceof Integer i) return "int " + i;
if (o instanceof String s) return "string [" + s + "]";
return "unknown";
}
public static void main(String[] args) {
System.out.println(describe(42));
System.out.println(describe("hello"));
System.out.println(describe(3.14));
}
}**Output**
int 42
string [hello]
unknown**Note:** the binding variable `i` / `s` is only in scope after the test passes.
Example 4 β `AbstractError.java`: cannot `new` an abstract class
public class AbstractError {
static abstract class Vehicle { abstract void move(); }
// new Vehicle(); // compile error
static class Car extends Vehicle {
@Override void move() { System.out.println("car rolls"); }
}
public static void main(String[] args) {
Vehicle v = new Car();
v.move();
}
}**Output**
car rollsCommon Mistakes
- Forgetting that you can't instantiate an `abstract` class
- Downcasting without checking β `ClassCastException` at runtime
- Confusing overloading with overriding (overloading is compile-time, overriding is runtime)
- Leaving an abstract method unimplemented in a concrete subclass β compile error
- Reaching for `instanceof` to switch on type β prefer polymorphism
Summary
- Parent reference + child object β dynamic dispatch
- `abstract` defines a contract; subclasses must fulfill it
- Pattern matching `instanceof` is the modern, type-safe form
Practice
# Practice - 09. Polymorphism
## Exercise 1 β `Pay` hierarchy
- File: `Homework01.java`
- Key concepts: abstract class, override
Requirements
- Abstract class `Pay` with abstract method `long pay()`.
- `Hourly(int hours, int rate)` and `Salary(long monthly)` extend it.
- In main, store both in `Pay[]` and print each `pay()`.
Expected output
hourly: 200000
salary: 3000000## Exercise 2 β Pattern-matching describe
- File: `Homework02.java`
- Key concepts: `instanceof` pattern matching
Requirements
- Write `static String describe(Object o)` for Integer / Double / String / else.
Expected output
int 7
double 3.140
string [hi]
unknown## Solutions After trying it yourself, compare with [`answer/`](./answer/).
Solution code (homework/answer/)
answer/Homework01.java
/** Pay hierarchy. */
public class Homework01 {
public static void main(String[] args) {
Pay[] payroll = { new Hourly(10, 20_000), new Salary(3_000_000) };
String[] names = { "hourly", "salary" };
for (int i = 0; i < payroll.length; i++) {
System.out.println(names[i] + ": " + payroll[i].pay());
}
}
}
abstract class Pay { abstract long pay(); }
class Hourly extends Pay {
int hours, rate;
Hourly(int h, int r) { this.hours = h; this.rate = r; }
@Override long pay() { return (long) hours * rate; }
}
class Salary extends Pay {
long monthly;
Salary(long m) { this.monthly = m; }
@Override long pay() { return monthly; }
}
answer/Homework02.java
/** Pattern-matching describe. */
public class Homework02 {
public static void main(String[] args) {
System.out.println(describe(7));
System.out.println(describe(3.14));
System.out.println(describe("hi"));
System.out.println(describe(true));
}
static String describe(Object o) {
if (o instanceof Integer i) return "int " + i;
if (o instanceof Double d) return String.format("double %.3f", d);
if (o instanceof String s) return "string [" + s + "]";
return "unknown";
}
}
Try It Yourself
cd 02_oop/09_polymorphism/src
javac ShapeDemo.java
java ShapeDemoNext Lecture
[10_Interface](../10_μΈν°νμ΄μ€/) β `interface`, `implements`, `default` methods.
All lecture materials and example code are openly available on GitHub.
View on GitHub β