RAD Studio (Common)
ContentsIndex
PreviousUpNext
Implementing Interfaces

Once an interface has been declared, it must be implemented in a class before it can be used. The interfaces implemented by a class are specified in the class's declaration, after the name of the class's ancestor.  

 

Such declarations have the form

type className = class (ancestorClass, interface1, ..., interfacen)
    memberList
end;

For example,

type
    TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
    .
    .
    .
end;

declares a class called TMemoryManager that implements the IMalloc and IErrorInfo interfaces. When a class implements an interface, it must implement (or inherit an implementation of) each method declared in the interface. 

Here is the (Win32) declaration of TInterfacedObject in the System unit. .

      type
        TInterfacedObject = class(TObject, IInterface)
        protected
        FRefCount: Integer;
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
        public
        procedure AfterConstruction; override;
        procedure BeforeDestruction; override;
        class function NewInstance: TObject; override;
        property RefCount: Integer read FRefCount;
     end;

TInterfacedObject implements the IInterface interface. Hence TInterfacedObject declares and implements each of the three IInterface methods. 

Classes that implement interfaces can also be used as base classes. (The first example above declares TMemoryManager as a direct descendent of TInterfacedObject.) On the Win32 platform, every interface inherits from IInterface, and a class that implements interfaces must implement the QueryInterface, _AddRef, and _Release methods. The System unit's TInterfacedObject implements these methods and is thus a convenient base from which to derive other classes that implement interfaces.  

When an interface is implemented, each of its methods is mapped onto a method in the implementing class that has the same result type, the same calling convention, the same number of parameters, and identically typed parameters in each position. By default, each interface method is mapped to a method of the same name in the implementing class.

You can override the default name-based mappings by including method resolution clauses in a class declaration. When a class implements two or more interfaces that have identically named methods, use method resolution clauses to resolve the naming conflicts. 

A method resolution clause has the form

procedure interface.interfaceMethod = implementingMethod;

or

function interface.interfaceMethod = implementingMethod;

where implementingMethod is a method declared in the class or one of its ancestors. The implementingMethod can be a method declared later in the class declaration, but cannot be a private method of an ancestor class declared in another module. 

For example, the class declaration

type
    TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
    function IMalloc.Alloc = Allocate;
    procedure IMalloc.Free = Deallocate;
    .
    .
    .
end;

maps IMalloc's Alloc and Free methods onto TMemoryManager's Allocate and Deallocate methods. 

A method resolution clause cannot alter a mapping introduced by an ancestor class.

Descendant classes can change the way a specific interface method is implemented by overriding the implementing method. This requires that the implementing method be virtual or dynamic. 

A class can also reimplement an entire interface that it inherits from an ancestor class. This involves relisting the interface in the descendant class' declaration. For example,

type
    IWindow = interface
    ['{00000115-0000-0000-C000-000000000146}']
    procedure Draw;
    .
    .
    .
end;
    TWindow = class(TInterfacedObject, IWindow)// TWindow implements IWindow
    procedure Draw;
    .
    .
    .
end;
TFrameWindow = class(TWindow, IWindow)// TFrameWindow reimplements IWindow
    procedure Draw;
    .
    .
    .
end;

Reimplementing an interface hides the inherited implementation of the same interface. Hence method resolution clauses in an ancestor class have no effect on the reimplemented interface.

On the Win32 platform, the implements directive allows you to delegate implementation of an interface to a property in the implementing class. For example,

property MyInterface: IMyInterface read FMyInterface implements IMyInterface;

declares a property called MyInterface that implements the interface IMyInterface

The implements directive must be the last specifier in the property declaration and can list more than one interface, separated by commas. The delegate property

  • must be of a class or interface type.
  • cannot be an array property or have an index specifier.
  • must have a read specifier. If the property uses a read method, that method must use the default register calling convention and cannot be dynamic (though it can be virtual) or specify the message directive.
The class you use to implement the delegated interface should derive from TAggregationObject.

Delegating to an Interface-Type Property (Win32 only)

If the delegate property is of an interface type, that interface, or an interface from which it derives, must occur in the ancestor list of the class where the property is declared. The delegate property must return an object whose class completely implements the interface specified by the implements directive, and which does so without method resolution clauses. For example,

type
 IMyInterface = interface
 procedure P1;
    procedure P2;
end;
TMyClass = class(TObject, IMyInterface)
 FMyInterface: IMyInterface;
 property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
end;
var
 MyClass: TMyClass;
 MyInterface: IMyInterface;
begin
 MyClass := TMyClass.Create;
 MyClass.FMyInterface := ...// some object whose class implements IMyInterface
 MyInterface := MyClass;
 MyInterface.P1;
end;

 

Delegating to a Class-Type Property (Win32 only)

If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched. Thus it is possible to implement some methods in the class specified by the property, and others in the class where the property is declared. Method resolution clauses can be used in the usual way to resolve ambiguities or specify a particular method. An interface cannot be implemented by more than one class-type property. For example,

type
 IMyInterface = interface
 procedure P1;
 procedure P2;
end;
TMyImplClass = class
 procedure P1;
 procedure P2;
end;
 TMyClass = class(TInterfacedObject, IMyInterface)
 FMyImplClass: TMyImplClass;
 property MyImplClass: TMyImplClass read FMyImplClass implements IMyInterface;
 procedure IMyInterface.P1 = MyP1;
 procedure MyP1;
end;
 procedure TMyImplClass.P1;
 .
    .
    .
 procedure TMyImplClass.P2;
 .   
    .
 . 
 procedure TMyClass.MyP1;
    .
    .
    .
var
 MyClass: TMyClass;
 MyInterface: IMyInterface;
begin
 MyClass := TMyClass.Create;
 MyClass.FMyImplClass := TMyImplClass.Create;
 MyInterface := MyClass;
 MyInterface.P1;    // calls TMyClass.MyP1;
 MyInterface.P2;    // calls TImplClass.P2;
end;
Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!