オブジェクト インターフェイス(Delphi)
オブジェクト インターフェイス(または単にインターフェイス)は、クラスに実装させることができるメソッドを定義します。インターフェイスはクラスと同じように宣言しますが、直接インスタンス化することはできません。また、インターフェイスが個々のメソッド定義を持つことはありません。つまり、インターフェイスをサポートするクラス側でインターフェイス メソッドの実装を提供する必要があります。インターフェイス型の変数は、オブジェクトのクラスがそのインターフェイスを実装している場合にオブジェクトを参照できますが、このような変数を使って呼び出すことができるのは、そのインターフェイスで宣言されたメソッドだけです。
インターフェイスを使用すると、意味上の難しさを回避しながら多重継承のメリットの一部を得ることができます。また、SOAP のような分散オブジェクト モデルを使用する場合にも重要な役割を果たします。インターフェイスをサポートするカスタム オブジェクトは、分散型オブジェクトを使用して、C++ や Java などの言語で書かれたオブジェクトと相互通信できます。
目次
インターフェイス型
インターフェイスは、クラスの場合と同様に、プログラムやユニットの一番外側のスコープでのみ宣言できます。手続きや関数内では宣言ではできません。インターフェイス型の宣言は、次の形式で記述します。
type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;
- 警告:
ancestorInterface
と GUID の指定は、Win32 COM との相互運用のサポートに必要です。COM からインターフェイスにアクセスする場合は、ancestorInterface
と GUID を指定してください。
インターフェイス宣言はクラス宣言とそれほど変わりませんが、次の制限事項があります。
- メソッドとプロパティのみ memberList に指定できる。フィールドをインターフェイスで使用できない。
- インターフェイスにはフィールドがないため、プロパティの read 指定子と write 指定子をメソッドにする必要がある。
- インターフェイスのメンバはすべて public となる。可視性指定子と格納指定子は使用できない (ただし、配列プロパティは default として宣言できる)。
- インターフェイスにはコンストラクタとデストラクタがない。インターフェイスのメソッドを実装しているクラスを使用する以外は、インターフェイスのインスタンス化はできない。
- メソッドは virtual、dynamic、abstract、override として宣言できない。インターフェイス自体はメソッドを実装していないため、これらの宣言に意味はない。
次に、インターフェイスの宣言の例を示します。
type IMalloc = interface(IInterface) ['{00000002-0000-0000-C000-000000000046}'] function Alloc(Size: Integer): Pointer; stdcall; function Realloc(P: Pointer; Size: Integer): Pointer; stdcall; procedure Free(P: Pointer); stdcall; function GetSize(P: Pointer): Integer; stdcall; function DidAlloc(P: Pointer): Integer; stdcall; procedure HeapMinimize; stdcall; end;
インターフェイスの宣言によっては、interface の予約語の代わりに dispinterface が使用されます。
IInterface と継承
インターフェイスは、クラスと同じように自身の上位インターフェイスのメソッドをすべて継承します。ただし、メソッドを実装しない点がクラスとは異なります。インターフェイスが継承するものは、メソッドの実装の必要性で、そのインターフェイスをサポートするすべてのクラスに渡されます。
インターフェイスを宣言するときは、上位インターフェイスを指定できます。上位インターフェイスを指定しない場合、そのインターフェイスは IInterface の直下の下位インターフェイスとして位置づけられます。IInterface は、System ユニットに定義されており、他のすべてのインターフェイスの最上位に位置づけられています。Win32 では、IInterface に 、QueryInterface、_AddRef、および _Release の 3 つのメソッドを宣言します。
QueryInterface
は、オブジェクトがサポートしている各インターフェイスを参照するための手段を提供します。_AddRef
と _Release
は、インターフェイス参照の存続期間のメモリ管理に使用されます。これらのメソッドを実装するときは、System
ユニットの TInterfacedObject
から実装クラスを派生させる方法が最も簡単です。また、どのメソッドも空の関数として実装することで破棄できます。ただし、COM オブジェクトは、_AddRef
と _Release
を使って管理する必要があります。
- 警告:
QueryInterface
、_AddRef
、および_Release
は、Win32 COM との相互運用のサポートに必要です。COM からインターフェイスにアクセスする場合は、必ずこれらのメソッドを実装する必要があります。
インターフェイスの識別と GUID
インターフェイス宣言では、GUID(グローバル一意識別子)を指定できます。GUID は、メンバ リスト直前の角かっこで囲んだ文字列リテラルで表現されます。宣言の GUID 部は、次の形式にする必要があります。
['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
各 x は 1 桁の 16 進数(0 ~ 9、A ~ F)です。タイプ ライブラリ エディタでは、新しいインターフェイスの GUID が自動的に生成されます。コード エディタで Ctrl+Shift+G を押しても GUID を生成できます。
GUID は、インターフェイスを一意に識別する 16 バイトのバイナリ値です。インターフェイスが GUID を持つ場合は、インターフェイス問い合わせを使用してその実装への参照を取得できます。
- メモ: GUID は、COM との相互運用にのみ使用されます。
System ユニットで宣言される TGUID 型と PGUID 型は、GUID の操作に使用します。
type PGUID = ^TGUID; TGUID = packed record D1: Cardinal; D2: Word; D3: Word; D4: array[0..7] of Byte; end;
Supports は次の 2 つの方法で呼び出せます。
if Supports(Allocator, IMalloc) then ...
または
if Supports(Allocator, IID_IMalloc) then ...
インターフェイスの呼び出し規約
インターフェイス メソッドのデフォルトの呼び出し規約は register ですが、インターフェイスがモジュール間で共有されている場合、特にモジュールが異なる言語で記述されている場合は、すべてのメソッドを stdcall で宣言する必要があります。Win32 では、safecall を使用して、デュアル インターフェイスのメソッドを実装できます。
インターフェイスのプロパティ
インターフェイスで宣言されたプロパティは、インターフェイス型の式を介してのみアクセスできます。クラス型変数からはアクセスできません。また、インターフェイスのプロパティは、インターフェイスがコンパイルされているプログラムの中からのみ参照できます。
インターフェイスではフィールドを使用できないため、プロパティの read 指定子と write 指定子をメソッドにする必要があります。
前方宣言
インターフェイスの宣言が予約語 interface とセミコロンだけで終わっており、上位インターフェイス、GUID、メンバ リストの指定が省略されている場合、その宣言は前方参照宣言になります。前方参照宣言は、同じ型宣言セクションの中で同じインターフェイスの宣言を定義することによって解決する必要があります。つまり、前方宣言とその定義宣言の間には、他の型宣言しか含められません。
forward 宣言を使用することで、相互に依存したインターフェイスを使用できるようになります。たとえば、次のようになります。
type IControl = interface; IWindow = interface ['{00000115-0000-0000-C000-000000000044}'] function GetControl(Index: Integer): IControl; //. . . end; IControl = interface ['{00000115-0000-0000-C000-000000000049}'] function GetWindow: IWindow; //. . . end;
相互派生のインターフェイスは許可されていません。たとえば、IControl から IWindow を派生させると同時に、IWindow から IControl を派生させることはできません。