I have problem with my code, which uses generic types. Why doesn't the compiler know that the passed list (Result) is a TObjectList<TItem> (TItem is type for T in TItems)?
Interface:
type
TItem = class
end;
type
IItemsLoader = interface
procedure LoadAll(AList : TObjectList<TItem>);
end;
type
TItemsLoader = class(TInterfacedObject, IItemsLoader)
public
procedure LoadAll(AList : TObjectList<TItem>);
end;
type
IItems<T : TItem> = interface
function LoadAll : TObjectList<T>;
end;
type
TItems<T : TItem> = class(TInterfacedObject, IItems<T>)
private
FItemsLoader : TItemsLoader;
public
constructor Create;
destructor Destroy; override;
function LoadAll : TObjectList<T>;
end;
Implementation:
procedure TItemsLoader.LoadAll(AList: TObjectList<TItem>);
begin
/// some stuff with AList
end;
{ TItems<T> }
constructor TItems<T>.Create;
begin
FItemsLoader := TItemsLoader.Create;
end;
destructor TItems<T>.Destroy;
begin
FItemsLoader.Free;
inherited;
end;
function TItems<T>.LoadAll: TObjectList<T>;
begin
Result := TObjectList<T>.Create();
/// Error here
/// FItemsLoader.LoadAll(Result);
end;
You have to use a generic version of the Loader as well:
type
TItem = class
end;
type
IItemsLoader<T: TItem> = interface
procedure LoadAll(AList : TObjectList<T>);
end;
type
TItemsLoader<T: TItem> = class(TInterfacedObject, IItemsLoader<T>)
public
procedure LoadAll(AList : TObjectList<T>);
end;
type
IItems<T : TItem> = interface
function LoadAll : TObjectList<T>;
end;
type
TItems<T : TItem> = class(TInterfacedObject, IItems<T>)
private
FItemsLoader : TItemsLoader<T>;
public
constructor Create;
destructor Destroy; override;
function LoadAll : TObjectList<T>;
end;
implementation
{$R *.dfm}
procedure TItemsLoader<T>.LoadAll(AList: TObjectList<T>);
begin
/// some stuff with AList
end;
{ TItems<T> }
constructor TItems<T>.Create;
begin
FItemsLoader := TItemsLoader<T>.Create;
end;
destructor TItems<T>.Destroy;
begin
FItemsLoader.Free;
inherited;
end;
function TItems<T>.LoadAll: TObjectList<T>;
begin
Result := TObjectList<T>.Create();
/// Error here
FItemsLoader.LoadAll(Result);
end;
In the function with the error, Result is a TObjectList<T>, where T is some subclass of TItem, but the compiler doesn't know what specific class it is. The compiler has to compile it so that it's safe to run for any value of T. That might not be compatible with the argument type of LoadAll, which requires a TObjectList<TItem>, so the compiler rejects the code.
Suppose T is TItemDescendant, and the compiler allows the faulty code to compile and execute. If LoadAll calls AList.Add(TItem.Create), then AList will end up holding something that isn't a TItemDescendant, even though it's a TObjectList<TItemDescendant>. It holds an object of a type different from what its generic type parameter says it holds.
Just because S is a subtype of T doesn't mean that X<S> is a subtype of X<T>.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With