目次
インターフェイスを宣言したら、使用する前にクラスに実装する必要があります。クラス宣言では、クラスによって実装されるインターフェイスが、クラスの祖先の名前の後に指定されます。
1. クラス宣言
このような宣言は次の形式になります。
type className = class (ancestorClass, interface1, ..., interfaceN)
memberList
end;
例えば:
type
TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
// ...
IMalloc インターフェイスと IErrorInfo インターフェイスを実装する TMemoryManager というクラスを宣言します。クラスがインターフェイスを実装するときは、インターフェイスで宣言されたすべてのメソッドを実装する (または実装から継承する) 必要があります。
System.TInterfacedObject の宣言は次のとおりです (Windows プラットフォームと他のプラットフォームでは宣言が若干異なります)。
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 は IInterface インターフェイスを実装します。したがって、TInterfacedObject は 3 つの IInterface メソッドをそれぞれ宣言し、実装します。
インターフェースを実装したクラスを基底クラスとして使用することもできます。(上記の最初の例では、TMemoryManager を TInterfacedObject の直接継承として宣言しています)。各インターフェイスは IInterface を継承し、インターフェイスを実装するクラスは QueryInterface、_AddRef、および _Release メソッドを実装する必要があります。ユニット System の TInterfacedObject はこれらのメソッドを実装するため、インターフェイスを実装する他のクラスを派生できる便利な基本クラスです。
インターフェイスが実装されると、インターフェイスの各メソッドは、同じ結果の型、同じ呼び出し規約、同じ数のパラメータ、および各位置の同じタイプのパラメータを持つ実装クラスのメソッドにマップされます。デフォルトでは、各インターフェイス メソッドは、実装クラス内の同じ名前のメソッドにマップされます。
2. 方法解決条項
デフォルトの名前ベースのマッピングは、クラス宣言にメソッド解決句を含めることによってオーバーライドできます。クラスが同じ名前のメソッドを持つ 2 つ以上のインターフェイスを実装する場合、メソッド解決句を使用して名前の競合を解決できます。
メソッド解決句の形式は次のとおりです。
procedure interface.interfaceMethod = implementingMethod;
または
function interface.interfaceMethod = implementingMethod;
ここで、implementingMethod は、このクラスまたは祖先クラスで宣言されたメソッドです。implementingMethod は、クラス宣言の後半で宣言されるメソッドにすることができますが、別のモジュールで宣言された祖先クラスのプライベート メソッドにすることはできません。
例えば:
type
TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
function IMalloc.Alloc = Allocate;
procedure IMalloc.Free = Deallocate;
// ...
end;
IMalloc の Alloc メソッドと Free メソッドを TMemoryManager の Allocate メソッドと Deallocate メソッドにマップします。
メソッド解決句は、親クラスによって導入されたマッピングを変更できません。
3. 継承された実装の変更
サブクラスは、実装メソッドをオーバーライドすることによって、特定のインターフェイス メソッドの実装方法を変更できます。これには、実装メソッドが仮想または動的である必要があります。
クラスは、祖先クラスから継承したインターフェイス全体を再実装することもできます。これには、サブクラスの宣言でインターフェイスを再リストする必要があります。例えば:
type
IWindow = interface
['{00000115-0000-0000-C000-000000000146}']
procedure Draw;
// ...
end;
TWindow = class(TInterfacedObject, IWindow)
// TWindow implements IWindow pocedure Draw;
// ...
end;
TFrameWindow = class(TWindow, IWindow)
// TFrameWindow reimplements IWindow procedure Draw;
// ...
end;
インターフェイスを再実装すると、同じインターフェイスの継承された実装が非表示になります。したがって、親クラスのメソッド解決句は、再実装されたインターフェイスには影響しません。
4. 委任によるインターフェースの実装
implements ディレクティブを使用すると、インターフェイスの実装を実装クラスのプロパティに委任できます。例えば:
property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
インターフェイス IMyInterface を実装する MyInterface という名前のプロパティが宣言されています。
implements ディレクティブは属性宣言の最後の指定子である必要があり、カンマで区切って複数のインターフェイスをリストできます。デリゲートのプロパティ:
- クラスまたはインターフェイス型である必要があります。
- 配列プロパティにすることはできず、インデックス指定子を持つこともできません。
- 読み取り指定子が必要です。プロパティが読み取りメソッドを使用する場合、そのメソッドはデフォルトのレジスタ呼び出し規則を使用する必要があり、動的にすることはできず (仮想である場合もあります)、メッセージ命令を指定することもできません。
デリゲート インターフェイスの実装に使用されるクラスは、System.TAggregatedObject から派生する必要があります。
5. インターフェースタイプのプロパティへの委任
委任されたプロパティがインターフェイス型である場合、そのインターフェイスまたは派生インターフェイスは、プロパティを宣言するクラスの祖先のリストに表示される必要があります。委任されたプロパティは、そのクラスがimplements ディレクティブで指定されたインターフェイスを完全に実装し、メソッド解決句を含まないオブジェクトを返す必要があります。例えば:
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;
6. クラス型プロパティへの委任
委任されたプロパティがクラスの型である場合、外部クラスとその祖先を検索する前に、指定されたインターフェイスを実装するメソッドがクラスとその祖先で検索されます。したがって、一部のメソッドは属性で指定されたクラスに実装でき、他のメソッドは属性を宣言したクラスに実装できます。メソッド解決句は、あいまいさを解決したり、特定のメソッドを指定したりするために、通常の方法で使用できます。インターフェイスを複数のクラス タイプ プロパティで実装することはできません。例えば:
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;