This would seem fairly simple and maybe I am just missing a bit of syntax glue... Here is my simple generic (Delphi XE3) example:
unit Unit1;
interface
uses
generics.collections;
type
X = class
public
Id: Integer;
end;
XList<T : X> = class( TObjectList<T> )
function Find(Id: Integer) : T;
end;
Y = class(X)
end;
YList = class(XList<Y>)
end;
implementation
{ XList<T> }
function XList<T>.Find(Id: Integer): T;
var
t: X;
begin
for t in Self do
if t.Id = Id then
Result := t;
end;
end.
This will not compile with "[dcc32 Error] Unit1.pas(41): E2010 Incompatible types: 'Y' and 'X'". It is down to the line:
YList = class(XList<Y>)
end;
Y derives from X so why is there an issue?
Generic methods in non-generic classYes, you can define a generic method in a non-generic class in Java.
To call a generic method, you need to provide types that will be used during the method invocation. Those types can be passed as an instance of NType objects initialized with particular . NET types.
Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used. It is possible to use both generic methods and wildcards in tandem.
Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.
Alex's answer is the correct solution to the problem. And it also does well to return from the function as soon as the answer is known.
I'd like to expand on the answer with some more explanation. In particular I'd like to answer the question you asked in comments to Alex's answer:
As an aside ... why doesn't the original work? T is derived from X.
The problem code is here:
function XList<T>.Find(Id: Integer): T;
var
t: X;
begin
for t in Self do
if t.Id = Id then
Result := t;
end;
The way to think about generics, is to imagine what the code looks like when you instantiate the type and supply a concrete type parameter. In this case, let's substitute T
with Y
. Then the code looks like this:
function XList_Y.Find(Id: Integer): Y;
var
t: X;
begin
for t in Self do
if t.Id = Id then
Result := t;
end;
Now you have a problem at the line that assigns to Result
:
Result := t;
Well, Result
is of type Y
, but t
is of type X
. The relationship between X
and Y
is that Y
is derived from X
. So an instance of Y
is an X
. But an instance of X
is not a Y
. And so the assignment is not valid.
As Alex correctly pointed out, you need to declare the loop variable to be of type T
. Personally I would write the code like this:
function XList<T>.Find(Id: Integer): T;
begin
for Result in Self do
if Result.Id = Id then
exit;
Result := nil;
// or perhaps you wish to raise an exception if the item cannot be found
end;
This also deals with the problem that your search routine leaves its return value uninitialized in case the item is not found. That's a problem that you compiler would have warned about once you got the code as far as actually compiling. I do hope you enable compiler warnings, and deal with them when they appear!
I had to reimplement the Find method as follows to fix it:
{ XList<T> }
function XList<T>.Find(Id: Integer): T;
var
item: T;
begin
for item in Self do
if item.Id = Id then
Exit(item);
Result := nil;
end;
What´s important here is to replace the type used in the variable declaration from X
to T
.
Then I just renamed the variable from t
to item
to avoid a name collision with the type placeholder T
and raplaced the Result := item
by Exit(item)
to return the found item and quit the method.
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