Java Tutorial
Java Polymorphism
A superclass variable can hold a subclass object. When you call an overridden method, the version that runs is the object's real type — not the variable's declared type.
One variable, many shapes
Polymorphism ("many forms") lets you write code against a general type while the runtime picks the specific behavior. Declare Animal a = new Dog(); and a.speak() still prints woof.
Example
class Animal {
void speak() {
System.out.println("...");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("woof");
}
}
class Cat extends Animal {
@Override
void speak() {
System.out.println("meow");
}
}
public class Main {
public static void main(String[] args) {
Animal a = new Dog();
a.speak(); // woof
a = new Cat();
a.speak(); // meow
}
}Collections of a base type
The payoff is treating different subclasses the same way in a loop. You store Animal references and call one method; each object answers in its own way.
Example
class Animal {
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override void speak() { System.out.println("woof"); }
}
class Cat extends Animal {
@Override void speak() { System.out.println("meow"); }
}
public class Main {
public static void main(String[] args) {
Animal[] zoo = { new Dog(), new Cat(), new Dog() };
for (Animal a : zoo) {
a.speak();
}
}
}Override vs overload
Overriding replaces a superclass method with the same signature; the JVM chooses it at runtime. Overloading is several methods with the same name but different parameters; the compiler chooses at compile time. Do not mix the terms.
Example — helper that takes Animal
class Animal {
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override void speak() { System.out.println("woof"); }
}
public class Main {
static void chorus(Animal a) {
a.speak(); // dynamic: depends on the real object
}
public static void main(String[] args) {
chorus(new Dog());
chorus(new Animal());
}
}The variable's type decides which methods you may call. The object's type decides which overrideruns. If only Dog has fetch(), an Animal variable cannot call it without a cast.
Try It Yourself
Exercise: Add a Bird subclass that prints "chirp". Put aDog, Cat, and Bird in an Animal[] and callspeak on each.
Show solution
class Animal {
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override void speak() { System.out.println("woof"); }
}
class Cat extends Animal {
@Override void speak() { System.out.println("meow"); }
}
class Bird extends Animal {
@Override void speak() { System.out.println("chirp"); }
}
public class Main {
public static void main(String[] args) {
Animal[] zoo = { new Dog(), new Cat(), new Bird() };
for (Animal a : zoo) a.speak();
}
}One loop, three behaviors — that is polymorphism in a sentence.
Key Takeaways
- A superclass reference can point at a subclass object.
- Overridden methods run based on the object's real type (runtime).
- Polymorphism lets you write loops and helpers against a general type.
- Overriding is not overloading — different times, different rules.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the Java editor at /java/try. Change one measurement and check whether the result still has the right unit.
Maths
Same call, different area
A Shape reference can hold a Square or a Circle. area() dispatches to the runtime type.
A = s² or A = πr²
Example
abstract class Shape {
abstract double area();
}
class Square extends Shape {
double s;
Square(double s) { this.s = s; }
double area() { return s * s; }
}
class Circle extends Shape {
double r;
Circle(double r) { this.r = r; }
double area() { return Math.PI * r * r; }
}
public class Main {
public static void main(String[] args) {
Shape p = new Circle(1.0);
System.out.println(p.area());
}
}