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