Aquila said:
I just read sun's generics-tutorial.pdf, it says that, for example, if
you have:
Unfortunately, just as I was about to reply with a link to that tutorial, I
re-read your question and saw that you already had it. That's a pity because
it's a reasonably clear explanation (IMO) though it needs a couple of readings
to make it settle. Try reading it again in a day to two.
public class Shape;
public class Circle extends Shape;
AND
public void drawAll(List<Shape> shapes);
AND you want to pass List<Circle> to drawAll(), you should use:
public void drawAll(List<? extends Shape> shapes);
instead.
Would somebody explain to me why it is that? Shoulnd't the "Shape" in
the first drawAll() definition imply all types derived from "Shape"??
Yes (in the sense I think you mean, anyway), but that only means that an
instance of Circle can be used anywhere where an instance of Shape is expected.
The same is /not/ true of Lists-of-Circles and Lists-of-Shapes.
If you use a List-of-Circles in a context where a List-of-Shapes is expected,
then it will break if the user of the (it thinks) List-of-Shapes decides to add
another Shape to the list. E.g. it might try to add a Triangle. As far as the
user of the List-of-Shapes is concerned, that is perfectly reasonable thing to
do, and should be legal. But the problem is that the list is /really/ a
List-of-Circles and its just not allowed to add Triangles to it.
That makes a problem for the type system. In a way it would seem to be
perfectly natural to allow a List-of-Circles to be used wherever a
List-of-Shapes is expected. That's because when you /read/ an item from the
list, expecting a Shape, you actually get a Circle, and that's all OK -- just
as it should be. The problem comes when you are allowed to add things to the
list.
So there are two ways out. One is to add some kind of mutability ("const" or
"readonly") declaration to the Java language. If that were added, then you
could indeed use a List-of-Circles wherever a List-of-Shapes was expected,
PROVIDED that the reference was "readonly".
Instead the Java designers decided to use the "wildcard" concept. I'm not sure
why since it seems to be more limited in scope, /and/ more convoluted in
application (though I'm sure the decision was not taken lightly). It has
roughly the same effect. By using a wildcard you are saying it's a
"List-of-Something(I don't know what the hell it is, exactly, but I /do/ know
that it extends Shape)". So you (the compiler's type checker) doesn't know
exactly what type of things to expect to find on the list, but it does know
that they all inherit from Shape, which means it's OK to read Shapes from the
list. But, since it doesn't know the precise type of the list, it has no idea
whether adding, say, a Triangle would be legal (it would be legal if the
"Something(I don't know what...)" were Shape, or Triangle, or Polygon, but not
it were Circle). Since the compiler can't prove to itself that modifying the
list is legal, it forbids it.
Simple ! Well, no -- I don't think it is simple. But maybe the above will
help the tutorial make sense when you next go over it.
-- chris
P.S. Not replying to the original question, but I can't help adding this
observation (or rant) for anyone who's interested: all the above complexity is
the result of a completely unnecessary (IMO) obsession with static type rigour.
The development type wasted ("wasted" IMO, of course) in creating this generics
system, plus the time that will be wasted by programmers learning to understand
and use it, plus the time that we will all waste creating/reading
complex/obscure generics declarations, could and (IMO) should have been put to
better use.