ジェネリックスの制約

提供: RAD Studio
移動先: 案内検索

ジェネリックス:インデックス への移動

制約は、ジェネリック型の型パラメータに関連付けできます。 制約は、ジェネリック型の構文のパラメータに渡される特定の型がサポートしなければならない項目を宣言します。

ジェネリック型を制約で指定する

制約項目には以下のものが含まれます:

  • 0、1、または複数個のインターフェイス型
  • 0 または 1 個のクラス型
  • 予約語 "constructor"、"class"、または "record"

1 つの制約について "constructor" と "class" の両方を指定できます。 これに対し、"record" は他の予約語と組み合わせできません。 複数の制約は、積(AND 論理)として作用します。

制約はすべての形式のジェネリック型に適用されます。ここの例ではクラス型のみを示します。

制約の宣言

制約の宣言は、通常のパラメータ リストの型宣言に類似しています。

 type
    TFoo<T: ISerializable> = class
      FField: T;
    end;

この宣言は、'T' 型パラメータが ISerializable インターフェイスをサポートしなければならないことを示しています。 TFoo<TMyClass> のような型構文では、コンパイラはコンパイル時に確認を行って、TMyClass が ISerializable を確実に実装するようにします。

複数の型パラメータ

制約を指定するときは、パラメータ リストの宣言と同じように、複数の型パラメータをセミコロンで区切ります。

 type
     TFoo<T: ISerializable; V: IComparable>

パラメータ宣言と同じように、複数の型パラメータをグループ化してコンマ リストにし、同じ制約にバインドできます。

 type
     TFoo<S, U: ISerializable> ...

上の例では、S と U が両方とも ISerializable 制約にバインドされます。

複数の制約

複数の制約を、コロンに続くコンマ リストとして単一の型パラメータに適用できます。

 type
     TFoo<T: ISerializable, ICloneable; V: IComparable> ...

制約された型パラメータは、"free" 型パラメータと混在させることができます。 たとえば、以下はすべて有効です。

 type
     TFoo<T; C: IComparable> ...
     TBar<T, V> ...
     TTest<S: ISerializable; V> ...
     // T と V は自由である。C と S は制約される

制約の型

インターフェイス型の制約

型パラメータ制約には、0、1、または複数のインターフェイス型のコンマ区切りリストを組み入れできます。

インターフェイス型で制約された型パラメータがある場合、コンパイラはコンパイル時に確認を行って、構文に引数として渡される具体的な型が指定のインターフェイス型を実装するようにします。

以下に例を示します。

 type
    TFoo<T: ICloneable> ...
  
    TTest1 = class(TObject, ICloneable)
       ...
    end;
  
    TError = class
    end;
  
  var
    X: TFoo<TTest1>;  // TTest1 は、コンパイル時にここで、ICloneable をサポートするか
                      // 確認される。
    Y: TFoo<TError>;  // 式: ここで構文エラー - TError はサポートしない
                      // ICloneable

クラス型制約

型パラメータは 0 または 1 個のクラス型によって制約できます。 インターフェイス型制約と同じように、この宣言がある場合、制約された型パラメータに引数として渡される具体的な型にはどの制約クラスとも代入互換性があることが、コンパイラで要求されます。

クラス型の互換性は、OOP 型互換性の通常のルールに従います。つまり、上位の型が要求される場合に下位の型を渡せます。

コンストラクタ制約

型パラメータは、予約語 "constructor" の 0 または 1 個のインスタンスによって制約できます。 つまり、実際の引数型は、デフォルトのコンストラクタ(パブリックなパラメータのないコンストラクタ)を定義するクラスである必要があります。その結果、ジェネリック型のメソッドは、引数型のデフォルトのコンストラクタを使用して引数型のインスタンスを作成できます。引数型については何も認識する必要はありません(最小基底型の要件なし)。

制約宣言では、インターフェイス型制約またはクラス型制約とともにどのような順序でも "constructor" を混在させられます。

クラス制約

型パラメータは、予約語 "class" の 0 または 1 個のインスタンスによって制約できます。 つまり、実際の型はクラス型でなければなりません。

レコード制約

型パラメータは、予約語 "record" の 0 または 1 個のインスタンスによって制約できます。 つまり、実際の型は値型でなければなりません(参照型ではありません)。 "record" 制約は、"class" または "constructor" 制約とは、組み合わせて使用できません。

型の推論

制約された型パラメータのフィールドまたは変数を使用するとき、多くの場合、フィールドまたは変数を制約された型の 1 つとして取り扱うために型キャストする必要はありません。 コンパイラはどの型が参照されているかを推論できます。そのために、コンパイラは、メソッド名を調べ、その型に対する全制約について同じ名前を共有するメソッドの結合についてさまざまなオーバーロード解決を実行します。

以下に例を示します。

 type
    TFoo<T: ISerializable, ICloneable> = class
      FData: T;
      procedure Test;
    end;
  
  procedure TFoo<T>.Test;
  begin
    FData.Clone;
  end;

コンパイラは ISerializable と ICloneable で "Clone" メソッドを探します。FData は型 T だからです。型 T はこれら両方のインターフェイスをサポートすることが保証されています。 両方のインターフェイスが同じパラメータ リストで "Clone" を実装する場合、コンパイラは、メソッド呼び出しがあいまいであるというエラーを出し、一方または他方のインターフェイスを型キャストしてコンテキストからあいまいさを除去するよう要求します。

関連項目