Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics, Polymorphism, Interfaces: what is the solution?

I know the title is Very wide - spanning over a lot !

And I hope that this question might evolve to a bigger "info wiki thingy" on the subjects.

What I have learned - so far:

  • When using Generics - understand the concepts (covariance and contravariance).
  • Do NOT "mis-use" the concept of generics combined with inheritance. I did and it could make you head directly into covariance problems! Make sure you "break off" the generic at the correct point in you inheritance - if you are combining the two.

(please correct me - if you think i'm wrong, missing or have misunderstood anything).

My problem was:

But by now I've spend countless hours, trying to figure out, how to solve this "big puzzle" I have on my desk. And I've gotten some good answers from several of you SO users already - but now its time to get something working in a bigger scale.

I ventured into Generics with this one: Generics and Polymorphism working together

And now I'm kinda stuck on this one: Situations where Generics won't work

Why I end up with covariance problems - is because of my class procedure in my hierarchy.

So I'm wondering if Interfaces is my next bold move in this "saga". How do one "step over" a covariance problem. One thing is to find out that you actually have this problem - another thing is "how to work around it".

So IF any of you good people "out there" has any opinions on this - I'm all ears. Basically : Tell me to go for Interfaces (I have never done one from scratch myself). Or .. throw me a bone in the direction you would suggest.

My current source pool is as stated in the second link - from the top.

Here is a small snippet from my earlier post that shows my covariance problem. David kindly explained - Why I ran into the bush.. But now I need info on - How to run around it.

var    
  aList : TBaseList<TBaseObject>;  // used as a list parameter for methods
  aPersonList : TPersonList<TPerson>;
  aCustomerList : TCustomerList<TCustomer>;
begin
  aPersonList := TPersonList<TPerson>.Create;
  aCustomerList := TCustomerList<TCustomer>.Create;

  aList := aCustomerList;  <-- this FAILS !!  types not equal ..

end;

Regards

like image 290
Bimmer_R Avatar asked Feb 08 '12 11:02

Bimmer_R


1 Answers

You can't do what you want to do, but that is not how you use generics anyway. As Rob Kennedy said, it makes no sense to have a TCustomerList<TCustomer> and a TPersonList<TPerson>. The beauty of generics is that you can use the same list for different element types. That means that list and element type must not have any dependencies.

You can do something like:

procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>);
begin
  // process the list using code that is independent of the actual type of T.
end;

...

var
  aCustomerList: TBaseList<TCustomer>;
  aPersonList: TBaseList<TPerson>;
begin
  ProcessList(aCustomerList);
  ProcessList(aPersonList);

Perhaps you may have to specify T (some early versions of generics did not handle type inference -- i.e. that it inferes the type of T from the type of the parameter -- very well), i.e.

  ProcessList<TCustomer>(aCustomerList);
  ProcessList<TPerson>(aPersonList);

But that, or something similar, is what you should do. Anything else doesn't make sense, IMO. There is no need to have a variable that could hold any of these lists, like your aList. And if you really need one, you can only use TObject, but that doesn't allow you to use the list in any useful way. And it is not very generic.

Interfaces won't help you at all with this problem. You can give classes certain capabilities, i.e. also the elements of the lists, through interfaces (another kind of polymorphism). But that won't handle covariance.

like image 86
Rudy Velthuis Avatar answered Sep 28 '22 16:09

Rudy Velthuis