Polymorphism (poly - multiple, morph - change)
The ability of a descendant class to take on the identity of it's ancestor. Polymorphism can only be supported through true inheritance. That is, all the properties and methods of an ancestor class must be inherited by the descendant.
If a descendant class overrides a method of the ancestor, the descendant's method is called regardless if the descendant class is being used directly or if it is acting as it's ancestor.
For example, suppose you have a drawing object called
TShape and has a method called
Draw which draws itself on a drawing surface (for example, a drawing context on Windows or a Canvas object in Delphi) at a point (X, Y) which are specified by two properties,
X and
Y.
type
TShape = class
private
FX : Integer;
FY : Integer;
public
constructor Create (X, Y : Integer); virtual;
procedure Draw (Canvas : TCanvas); virtual;
property X : Integer read FX write FX;
property Y : Integer read FY write FY;
end;
You also define two classes,
TCircle and
TRectangle which both decend from
TShape. In
TCircle, you add a
Radius property and override the
Draw method to draw the circle with it's center at (X, Y) and a radius of
Radius. You define
TRectangle with
Width and
Height properties and also override the
Draw method to draw the rectangle with a width
Width, a height
Height, and it's upper left corner at (X, Y).
type
TCircle = class
private
FRadius : Double;
public
constructor Create (X, Y : Integer; Radius : Double); virtual;
procedure Draw (Canvas : TCanvas); override;
property Radius : Double read FRadius write FRadius;
end;
TRectangle = class
private
FHeight : Integer;
FWidth : Integer;
public
constructor Create (X, Y, Width, Height : Integer); virtual;
procedure Draw (Canvas : TCanvas); override;
property Height : Integer read FHeight write FHeight;
property Width : Integer read FWidth write FWidth;
end;
What can you do with this? Lets say you have various circles and rectangles which you want drawn. Instead of having two arrays, one an array of
TCircle and another an array of
TRectangle, you can take advantage of polymorphisim and have a single array of
TShape objects which you place the various
TCircle and
TRectangle objects.
:
:
var
Shapes : array [0..4] of TShape;
begin
Shapes[0] := TCircle.Create(100,50,8);
Shapes[1] := TRectangle.Create(56,345,50,50);
Shapes[2] := TRectangle.Create(210,90,5,100);
Shapes[3] := TCircle.Create(245,124,34);
Shapes[4] := TRectangle.Create(5,115,124,87);
end;
Because
TCircle and
TRectangle are polymorphic, you can assign then to a variable that is of type
TShape. Moreover, if you were to iterate through this array and call the
Draw method of
TShape, the appropriate
Draw method of
TCircle or
TRectangle would be called.
How is this accomplished? Part of being polymorphic requires that all descendant objects inherit all properties and methods of it's ancestor. In this case,
TCircle and
TRectangle inherit the
Create constructor, the
Draw method, and the
X and
Y properties. Because of this, you can utilize any property or method defined in the ancestor.
A common misconception is that polymorphism is the ability to override the ancestor methods. Although this ability makes polymorphism more powerful (and useful), it is not actually part of polymorphism. You could eliminate this ability and still have a polymorphic object.
How do languages (and Delphi in particular) know which method to call of a polymorphic object? When you create a class, an entity called a virtual method table (VMT) is also created. The VMT is nothing more then a list of methods and their pointers in the object. When a descendant class is declared, it gets a copy of it's ancestor's VMT and then adds it's own entries at the end for it's own methods. If a descendant overrides a method of it's ancestor, the descendant replaces the pointer to the original method in it's own VMT with the pointer to the new method.
So, when the
Draw method is called, it is looked up in the VMT and the pointer that cooresponds to the
Draw method is used. The mechinism that does this at run time does not know, or indeed care, whether or not the pointer is pointing to the original
Draw method of
TShape or the
Draw method of
TCircle and
TRectangle.