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.