Skip to main content
eLearner.app
Module 3 · Lesson 2 of 26/14 in the course~15 min
Module lessons (2/2)

Polymorphism and Abstract Classes

Polymorphism and abstract classes are advanced object-oriented programming concepts that allow us to write flexible, extensible code independent of specific implementations.

Polymorphism

The term polymorphism (many forms) refers to the ability to treat objects of different classes as if they belong to a common type. In Java, a superclass reference variable can store a reference to an object of any of its subclasses.

Code
class Animal {
    public void makeSound() {
        System.out.println("Verso...");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    public void makeSound() {
        System.out.println("Miao");
    }
}

Thanks to polymorphism, we can do this:

Code
Animal myAnimal1 = new Dog(); // Polymorphism
Animal myAnimal2 = new Cat(); // Polymorphism

myAnimal1.makeSound(); // Executes Dog's method (Woof)
myAnimal2.makeSound(); // Executes Cat's method (Miao)

The decision on which method to invoke is made at runtime (Late Binding or dynamic dispatch) based on the actual object, not the type of the reference variable.

Abstract Classes (abstract)

An abstract class is a class marked with the abstract keyword that cannot be directly instantiated (you cannot do new MyAbstractClass()). It serves as a "partial template" for other classes.

Abstract Methods

An abstract class can contain abstract methods: methods declared without a body (no curly braces and no code), ending with a semicolon. Non-abstract subclasses are required to implement all inherited abstract methods.

Code
abstract class Shape {
    String color;

    // Abstract method (no body)
    public abstract double getArea();
}

class Circle extends Shape {
    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // Required to implement getArea
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Dynamic Binding / Late Binding

How does Java know which method to execute at runtime? This process is known as Late Binding (or Dynamic Binding). Unlike overloading (which is resolved at compile-time), the compiler does not know which specific method will be executed. Instead, it generates a generic call instruction. At runtime, the Java Virtual Machine (JVM) inspects the actual object in memory and invokes the method implementation defined in that object's class.

Abstract Classes and Missing Implementation

If a subclass inherits from an abstract class, it has two options:

  1. Implement all abstract methods: in this case, the subclass can be a normal (concrete) class and can be instantiated.
  2. Declare itself abstract: if the subclass does not provide an implementation for all inherited abstract methods, it must also be declared with the abstract keyword.
Code
abstract class Animal {
    public abstract void makeSound();
}

// This class MUST be abstract because it does not implement makeSound()
abstract class Canine extends Animal {
    // Inherits makeSound() but does not implement it
}

Try it yourself

Exercise#java.m3.l2.e1
Attempts: 0Loading…

Declare a variable s of type Shape and assign a new Circle object to it using polymorphism.

Loading editor…
Show hint

Write `Shape s = new Circle();` to use the base type as the reference type.

Solution available after 3 attempts

Exercise#java.m3.l2.e2
Attempts: 0Loading…

Make the Shape class abstract and add the abstract method double getArea(). Then complete Square so that it extends Shape and implements getArea() returning side * side.

Loading editor…
Show hint

Declare `public abstract double getArea();` in Shape. In Square, add `@Override public double getArea() { return side * side; }`.

Solution available after 3 attempts

Exercise#java.m3.l2.e3
Attempts: 0Loading…

Declare in the main method an array of type Shape[] containing a Circle object with radius 2.0 and a Square object with side 3.0. Then, use a loop (for or for-each) to print the area of each shape by invoking the getArea() method.

Loading editor…
Show hint

Declare the array with `Shape[] shapes = { new Circle(2.0), new Square(3.0) };` and loop through it using `for (Shape s : shapes)` printing `s.getArea()`.

Solution available after 3 attempts