Polymorphism
How one call site runs different code depending on the actual object, and why that kills if/else chains.
Open this lesson in the learning hubKey points
- One declared type, many runtime types. The variable says
Shape; the object decides whicharea()runs. - Java picks the method from the actual object at runtime, not from the declared type. That is dynamic dispatch.
- This is how you delete type-checking if/else chains. Add a new class and existing code keeps working untouched.
- Overriding is runtime and polymorphic. Overloading (same name, different parameters) is resolved at compile time.
- Fields are not polymorphic. A subclass field with the same name hides the parent field, it does not override it.
Example
import java.util.List;
public class Main {
interface Shape { double area(); }
record Circle(double r) implements Shape { public double area() { return Math.PI * r * r; } }
record Square(double side) implements Shape { public double area() { return side * side; } }
// One method handles every Shape that will ever exist. No if/else on type.
static double totalArea(List<Shape> shapes) {
double sum = 0;
for (Shape s : shapes) sum += s.area(); // chosen at runtime
return sum;
}
public static void main(String[] args) {
List<Shape> shapes = List.of(new Circle(1), new Square(2), new Circle(3));
for (Shape s : shapes) {
System.out.printf("%-18s area=%.2f%n", s, s.area());
}
System.out.printf("total = %.2f%n", totalArea(shapes));
}
}
Program against the interface and new implementations slot in without touching old code.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.