Inheritance and Polymorphism are two of the three pillars of OOP (along with Encapsulation) that enable code reuse and dynamic behavior.
1. Inheritance (The “Is-A” Relationship)
Inheritance is the mechanism by which a new class (Descendant or Child Class) acquires the fields and methods of an existing class (Ancestor or Parent Class).
- Benefit: Reduces redundancy by placing common functionality in a single parent class.
- Syntax: When declaring a class, you specify its ancestor in parentheses. All classes ultimately inherit from
TObject.
program InheritanceDemo;
type
// Ancestor Class
TShape = class(TObject)
public
procedure Draw; virtual; // Declared virtual to allow overriding
procedure Move(X, Y: Integer);
end;
// Descendant Class (inherits Draw and Move from TShape)
TCircle = class(TShape)
public
// TCircle specializes the Draw method
procedure Draw; override; // Must use 'override'
end;
A. Implementing Inheritance
When a descendant implements a method inherited from its ancestor, it can choose to completely replace the parent’s logic or extend it using the inherited keyword.
procedure TShape.Move(X, Y: Integer);
begin
Writeln('Shape moved to (', X, ', ', Y, ')');
end;
procedure TCircle.Draw;
begin
// Call the ancestor's implementation first (optional, but common)
inherited Draw;
// Then add specialized behavior
Writeln('Drawing a filled circle.');
end;
2. Polymorphism (Many Forms)
Polymorphism is the ability for objects of different classes to respond to the same method call in a way that is specific to their own class. This is achieved through Virtual Methods.
A. Virtual Methods
A method must be declared virtual in the ancestor class to be eligible for Polymorphism. This tells the compiler to use a special table (VMT – Virtual Method Table) at runtime to determine which version of the method to execute.
| Keyword | Location | Purpose |
virtual | Declared in the Ancestor. | Marks a method as polymorphic; required for the compiler to use the VMT. |
override | Declared in the Descendant. | Indicates that this method replaces the behavior of an inherited virtual method. |
dynamic | (Less common) Similar to virtual but uses a slower, different lookup mechanism. | N/A |
B. The Polymorphic Call
Polymorphism only occurs when a method is called through a variable typed as an Ancestor class (or TObject).
var
S: TShape; // Ancestor reference variable
begin
// S is a TShape variable, but it holds a TCircle object
S := TCircle.Create;
// 1. Polymorphic call: The VMT checks the object's true type (TCircle)
S.Draw; // Executes TCircle.Draw
// 2. Non-Polymorphic call: Move was not virtual, so it executes the TShape version
S.Move(10, 10);
S.Free;
end.
3. Abstract Classes and Methods
Sometimes, a method in an ancestor class is conceptually necessary but cannot be meaningfully implemented in the ancestor itself (e.g., how can a generic TShape know how to CalculateArea?).
A. abstract Keyword
- A method declared
abstracthas a header but no implementation (begin...endblock). - Any class containing an
abstractmethod automatically becomes an Abstract Class. - Rule: You cannot instantiate (create an object of) an Abstract Class. You must inherit from it and override all
abstractmethods before you can create an instance.
type
TShape = class(TObject)
public
// The implementation for CalculateArea must be provided by descendants
function CalculateArea: Real; virtual; abstract;
end;
// TCircle must provide a concrete implementation for CalculateArea
TCircle = class(TShape)
public
function CalculateArea: Real; override;
end;
var
A: TShape;
begin
// A := TShape.Create; // ERROR: Cannot instantiate abstract class
A := TCircle.Create; // OK
end.
4. Interfaces (Design Contracts)
While inheritance focuses on the “is-a” relationship, Interfaces define a “can-do” or “contract” relationship. An Interface defines a list of methods that any class implementing it must provide.
- Syntax: Defined with the
interfacekeyword (different from the Unit interface section). Methods have no implementation. - Implement: A class uses the
implementsclause to state it fulfills the contract.
type
// Interface definition (starts with I)
IDataLogger = interface
['{GUID-HERE}'] // Unique ID, automatically generated in Delphi/Lazarus
procedure LogMessage(const Msg: String);
procedure LogError(const Error: String);
end;
// TFileLogger implements the contract defined by IDataLogger
TFileLogger = class(TInterfacedObject, IDataLogger)
public
// Must implement every method defined in the interface
procedure LogMessage(const Msg: String);
procedure LogError(const Error: String);
end;
Interfaces are powerful for design patterns, dependency injection, and advanced application architecture (covered further in advanced courses).
