Polymorphism and Dynamic Binding in Java

Whether you are completely new to Java, or you are a senior software engineer that has used Java for decades, you will have to understand what polymorphism is and how to use it. On the surface it may seem straightforward, but there are many complex features like static and dynamic binding. Understanding what polymorphism is and how to use it will help you to write better object oriented programs.

What is Polymorphism

Polymorphism means "many forms". In Java, it means many forms of a method.

For example, lets say we have a class called GeometricObject. It has 3 data fields for its color, date created, and whether it is filled or not. It has 2 constructors, accessors and mutators for the data fields, and a toString method.

import java.util.Date;

public class GeometricObject {
    private String color = "white";
    private boolean filled = false;
    private Date dateCreated = new Date();

    public GeometricObject() {
    }

    public GeometricObject(String color, boolean filled) {
        this.color = color;
        this.filled = filled;
    }

    public GeometricObject(String color) {
        this.color = color;
    }

    public GeometricObject(boolean filled) {
        this.filled = filled;
    }

    public Date getDateCreated() {
        return dateCreated;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public boolean isFilled() {
        return filled;
    }

    public void setFilled(boolean filled) {
        this.filled = filled;
    }

    @Override
    public String toString() {
        return "GeometricObject{" +
                "color='" + color + '\'' +
                ", filled=" + filled +
                ", dateCreated=" + dateCreated +
                '}';
    }
}

If we have a new class called Circle that is a subclass of GeometricObject, it will have the same data fields and methods as the superclass, but we will add one more data field called radius. We will also add 2 extra methods for getting the area and circumference of the circle.

public class Circle extends GeometricObject {
    private double radius = 1;

    public Circle() {}

    public Circle(String color, boolean filled, double radius) {
        super(color, filled);
        this.radius = radius;
    }

    public Circle(String color, boolean filled) {
        super(color, filled);
    }

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

    public double getArea() {
        return Math.PI * radius * radius;
    }

    public double getCircumference() {
        return 2 * Math.PI * radius;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                '}';
    }
}

The inheritance relationship between Circle and GeometricObject allows Circle to inherit methods from GeometricObject and add its own methods as well. Every instance of subclass is an instance of its superclass; therfore, an instance of a Circle is also an instance of a GeometricObject. Look at the following method called displayObject.

public static void displayObject(GeometricObject object) {
    System.out.println(object);
}

This method can not only be called with an instance of GeometricObject but also with an instance of Circle, because every instance of a Circle is also an instance of a GeometricObject. The following method call would be valid code:

displayObject(new Circle(3));

It would display:

Circle{radius=6.0}

The main thing to take away from this is that a supertype may also refer to a subclass of the supertype.

Dynamic Binding

Before we get into what dynamic binding is, it is important to distinguish between what the declared type and the acutal type of an object are.

Declared and Actual Type

A variable's declared type is the type used when the variable is declared.

Object myCircle = new Circle();

In the declaration above, the type Object is the declared type because it was the type used to declare the variable myCircle.

The actual type of a variable is the actual class that is referenced by the object. If we use the same declaration, Circle would be the actual type because myCircle is an instance of Circle.

The Idea Behind Dynamic Binding

To understand dynamic binding, you need to see it in action. Lets say we have a circle of radius 5, and we want to know if it is shaded or not.

Circle circle = new Circle(5);
System.out.println(circle.isFilled());

The first thing the JVM will do when it sees the print statement is check to see if the Circle has the implementation of the method isFilled. If it does, it will call that method. If it doesn't, it will look for the method in Circle's superclass GeometricObject and call that method.

In this case the method isFilled is not implemented in Circle, but it is implemented in GeometricObject, so it will call that method.

false

Another Example

Lets use the common toString method as another example of dynamic binding.

System.out.println(new Circle(2));

In this case, the JVM will look for the implementation of the toString method in Circle and will try to call that method. It is there and it is called.

Circle{radius:2.0}

If the implementation wasn't there, it would go to the next level (i.e. a higher superclass in the hierarchy) and look for the implementation of the toString method there; in this case, it would be GeometricObject. If the toString method wasn't there (because right now it is), it would go to the next level superclass, which is Object.

Method Matching and Method Binding

The declared type of a variable will decide which method to run at compile time. The compiler will find a method that matches the method signature at compile time; keep in mind that there may be several methods with the same signature but have different implementations in the inheritance hierarchy.

The JVM binds the implementation of the method to the actual type of the object at runtime.

Wrapping Up

Polymorphism and dynamic binding can be challenging concepts to grasp at first, but once you start working with them and become familiar with them, your code will become much more maintainable.