8.4 Generic type restrictions

The diagram in section 8.2, page 450 shows that the type template list can have extra specifiers for the types. This is especially useful for object types: if the template type must descend from a certain class, then this can be specified in the template list:

{$mode objfpc}  
{$h+}  
uses sysutils, classes;  
 
Type  
  generic TList<_T : TComponent> = class(TObject)  
  public  
    Type TCompareFunc = function(const Item1, Item2: _T): Integer;  
  Public  
    data : _T;  
    procedure Add(item: _T);  
    procedure Sort(compare: TCompareFunc);  
 end;

Given the above definition, the following will compile:

TPersistentList = specialize TList<TComponent>;

But this will not compile

TPersistentList = specialize TList<TPersistent>;

The compiler will return an error:

Error: Incompatible types: got "TPersistent" expected "TComponent"

Multiple types can be grouped together:

Type  
  generic TList<Key1,Key2 : TComponent; Value1 : TObject> = class(TObject)

Additionally, it is possible to specify more than one type identifier for class and interface type restrictions. If a class is specified, then the type used for the template must be equal to or descend from the indicated type:

Type  
  generic TList<T: TComponent, IEnumerable> = class(TObject)

A class used to specialize T must descend from TComponent and must implement IEnumerable.

If an interface is specified, then the template type must implement at least this interface, but it can also be a descendent interface from this interface:

Type  
  generic TGenList<T: IEnumerable> = class(TObject)  
 
  IMyEnum = Interface (IEnumerable)  
    Procedure DoMy;  
  end;  
 
  TList = specialize TGenList<IMyEnum>;  
  TSomeList = Specialize TGenList<TList>;

Multiple interfaces can be specified, in that case the class type must implement all listed interfaces: It is possible to mix one class name with several interface names.

If no type restrictions are in effect, the compiler will assume that template types are not assignment compatible.

This is specially important when the generic class contains overloaded methods. Given the following generic type declaration:

type  
  generic TTest<T1, T2> = class  
    procedure Test(aArg: LongInt);  
    procedure Test(aArg: T1);  
    procedure Test(aArg: T2);  
  end;

Specializing the above will compile if T1 and T2 are of two different types and neither is also LongInt. The following will compile:

T1 = specialize TTest<String, TObject>;

But the following two will not compile:

T2 = specialize TTest<String, String>;

or

T2 = specialize TTest<String, Longint>;