These things are known, in type theory, as variance, with <? extends T>
being a co-variant notation, and <? super T>
being a contra-variant notation. The simplest explanation is that ?
may be replaced by any type extending T
in the co-variant notation, and ?
may be replaced by any type which T
extends in the contra-variant one.
Using co and contra-variance is much more difficult than it may seem at first, particularly since the variance "toggles" depending on the position.
A simple example would be a function-class. Say you have a function which takes an A
and returns a B
. The correct notation for it would be to say that A
is contra-variant and B
os co-variant. To understand better how this is the case, let's consider a method -- let's call it g
-- which receives this hypothetical function class, where f is supposed to receive an Arc2D
and return a Shape
.
Inside g
, this f
is called passing an Arc2D
and the return value is used to initialize an Area
(which expects a Shape
).
Now, suppose that the f
you pass receives any Shape
and returns a Rectangle2D
. Since an Arc2D
is a also a Shape
, then g
won't get an error passing an Arc2D
to f
, and since a Rectangle2D
is also a Shape
, then it can be passed to Area
's constructor.
If you try to invert any of the variances or swap the expected and actual types in that example, you'll see it fails. I don't have the time right now to write down this code, and my Java is quite rusty at any rate, but I'll see what I can do later -- if no one is kind enough to do it first.