Java Polymorphism
Polymorphism means "many forms". In Java it means that a variable of a superclass type can hold an object of any of its subclasses, and when you call an overridden method, the version of the actual object runs. This lets one piece of code work with many different kinds of objects. It builds directly on Inheritance.
A superclass variable, many objects
class Shape {
double area() {
return 0;
}
}
class Circle extends Shape {
double r;
Circle(double r) { this.r = r; }
@Override
double area() {
return Math.PI * r * r;
}
}
class Square extends Shape {
double side;
Square(double side) { this.side = side; }
@Override
double area() {
return side * side;
}
}
public class Main {
public static void main(String[] args) {
Shape[] shapes = {new Circle(1), new Square(3), new Circle(2.5)};
double total = 0;
for (Shape s : shapes) {
System.out.printf("%s: %.2f%n", s.getClass().getSimpleName(), s.area());
total += s.area();
}
System.out.printf("Total area: %.2f%n", total);
}
}Circle: 3.14 Square: 9.00 Circle: 19.63 Total area: 31.78
The array has the type Shape[], yet it holds circles and squares. In the loop, s.area() runs the circle's or the square's method, depending on what s really is. Java decides this while the program runs; this is called dynamic dispatch.
The loop does not need to know which shapes exist. Add a Triangle subclass later and the loop works for it without any change. That is the main benefit of polymorphism.
Methods that accept any subclass
A method parameter of the superclass type accepts every subclass:
class Animal {
String sound() {
return "...";
}
}
class Cat extends Animal {
@Override
String sound() {
return "meow";
}
}
class Cow extends Animal {
@Override
String sound() {
return "moo";
}
}
public class Main {
static void speak(Animal a) {
System.out.println("The animal says " + a.sound());
}
public static void main(String[] args) {
speak(new Cat());
speak(new Cow());
speak(new Animal());
}
}The animal says meow The animal says moo The animal says ...
instanceof and casting
A Shape variable only lets you call what Shape has. To use something that only a subclass has, first check the real type with instanceof. The modern form checks and creates a variable of the subclass type in one step:
class Shape {
}
class Circle extends Shape {
double r = 2;
}
class Square extends Shape {
double side = 3;
}
public class Main {
public static void main(String[] args) {
Shape[] shapes = {new Circle(), new Square()};
for (Shape s : shapes) {
if (s instanceof Circle c) {
System.out.println("A circle with radius " + c.r);
} else if (s instanceof Square sq) {
System.out.println("A square with side " + sq.side);
}
}
}
}A circle with radius 2.0 A square with side 3.0
If you find yourself writing many instanceof checks, it is often a sign that the behaviour should be an overridden method instead. The Shape class above returns 0 for area(), which is not really meaningful; the next topic, Abstraction, shows how to say "every shape must have an area" properly.
Practice exercises
- Add a
Trianglesubclass (base and height) to the first example and put one in the array. - Write a class
Employeewith a methodpay(), and subclassesManagerandInternthat pay differently; print the total pay of a mixed array. - In the
speakexample, add aDogsubclass without changing thespeakmethod.