I'm using generics rather long time but I've never used construction like List<?开发者_运维问答 super T>
.
What does it mean? How to use it? How does it look after erasure?
I also wonder: is it something standard in generic programming (template programming?) or it's just a java 'invention'? Does c#, for example, allow similar constructions?
This construct is used when you want to consume items from a collection into another collection. E.g. you have a generic Stack
and you want to add a popAll
method which takes a Collection as parameter, and pops all items from the stack into it. By common sense, this code should be legal:
Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ... ;
numberStack.popAll(objects);
but it compiles only if you define popAll
like this:
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
The other side of the coin is that pushAll
should be defined like this:
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
Update: Josh Bloch propagates this mnemonic to help you remember which wildcard type to use:
PECS stands for producer-extends, consumer-super.
For more details, see Effective Java 2nd Ed., Item 28.
This is called "bounded wildcard". It's very well explained in the official tutorial.
As stated in the tutorial, you thus know that the list contains objects of exactly one subtype of T
For example List<? extends Number>
can hold only Integer
s or only Long
s, but not both.
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.
The Java Generics FAQ has a good explanation about Java generics. Check the question What is a bounded wildcard? which explains the usage of construct "? super T" in good detail.
精彩评论