Polymorphism and Dynamic Binding

(0) Introduction

Polymorphism and Dynamic Binding can be shown in situations such as when two classes inherit from a third.  We might have a superclass called Shape and both Square and Circle are subclasses of that.  We can have code written that doesn't know, until it's run, whether it will be dealing with a Square or a Circle.  We can however make the interface of both Square and Circle to be identical, so that it won't matter.  We can send exactly the same messages to either sort of Shape and it will respond in its own way.  For example we might sent a reportArea message to an instance of Square and it will respond by multiplying its length by itself (i.e.   l2).  It it were an instance of Circle that received that message it would respond by squaring its radius and multiplying by the constant π (i.e.   π r2)

This idea is the essence of polymorphism, where two objects of different types can accept exactly the same set of messages and respond to them in their own particular way. 

Exercise:  Draw a UML Class Diagram to show the relationship between the classes Shape, Square and Circle.

There are two slightly differently ways of programming the same situation as has been referred to above, using (1) Abstract Classes and (2) Interfaces.  These two ways are discussed below.

(1) Illustration of Abstract Classes and Polymorphism

SHAPE.JAVA

public abstract class Shape
{
    abstract void setDimension(double a);
    abstract double reportArea();
}

SQUARE.JAVA

public class Square extends Shape
{
    double l;

    public void setDimension(double amount)
    {
        l = amount;
    }

    public double reportArea()
    {
        return l * l;
    }
}

CIRCLE.JAVA

public class Circle extends Shape
{
    double r;

    public void setDimension(double amount)
    {
        r = amount;
    }

    public double reportArea()
    {
        return 3.14 * r * r;
    }
}

TEST.JAVA

public class Test
{
    public static void main(String [] args)
    {
        Shape x;
        double dim;
        int choice;

        System.out.println("If you have a square, type 0; if a circle, type another integer.\n");

        choice = Keyboard.readInt();

        if(choice == 0)
            x = new Square();
        else
            x = new Circle();

        System.out.println("Please type the dimension of your shape, side in case of square, radius in case of circle.\n");

        dim = Keyboard.readInt();
        x.setDimension(dim);
        System.out.println(" " + x.reportArea());

    }
}

(2) Illustration of Interfaces and Polymorphism

SHAPE.JAVA

public interface Shape
{
    void setDimension(double a);
    double reportArea();
}

SQUARE.JAVA

public class Square implements Shape
{
    double l;

    public void setDimension(double amount)
    {
        l = amount;
    }

    public double reportArea()
    {
        return l * l;
    }
}

CIRCLE.JAVA

public class Circle implements Shape
{
    double r;

    public void setDimension(double amount)
    {
        r = amount;
    }

    public double reportArea()
    {
        return 3.14 * r * r;
    }
}

TEST.JAVA

public class Test
{
    public static void main(String [] args)
    {
        Shape x;
        double dim;
        int choice;

        System.out.println("If you have a square, type 0; if a circle, type another integer.\n");

        choice = Keyboard.readInt();

        if(choice == 0)
            x = new Square();
        else
            x = new Circle();

        System.out.println("Please type the dimension of your shape, side in case of square, radius in case of circle.\n");

        dim = Keyboard.readInt();
        x.setDimension(dim);
        System.out.println(" " + x.reportArea());

    }
}

(3) What Polymorphism is all about

Note that it's only at run time that x becomes either a Square or a Circle. That's because it's up to the user to specify this. According to what type x turns out to be, the setDimension and reportArea method calls at the very end of the above code will either be to those belonging to Square or those belonging to Circle depending on what x ended up being. All that can be known in advance is that the method calls are to some Shape, but we don't know which of the two implementations. We say that the particular implementation is dynamically bound at run time, based on what the user chose.  (This is what people mean when they use the phrase Dynamic Binding.) 

What this means that anywhere that I have a Shape object, I can substitute a Circle or a Square, and everything should run fine.  This is the essence of polymorphism.  There is one setDimension operation and one reportArea operation, but a number of methods to implement each of them.  The subtypes conform to the supertype's interface. 

(4) Why use abstract classes and why use interfaces

In general abstract classes are used when we've some common functionality that we'd like inheriting classes to have.  In other words the functionality and features that are common to both (or all) the inheriting classes is pulled out into a superclass. 

Interfaces have a rather different role.  They're used when we wish to establish a contract that all implementing classes are to fulfil.  In fact, interfaces don't even allow method bodies to be defined in the interface. 

It's clear that, in the above discussion, (sections (1) and (2)), that, in this case, it would be more appropriate to use the idea of interface than abstract classes, and so in retrospect, section (1) ought, so to speak, to be deleted since abstraction is used clumsily here.