Can someone explain what does <? super T> mean

2019-01-23 03:27发布

问题:

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?

回答1:

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.



回答2:

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 Integers or only Longs, but not both.



回答3:

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.



回答4:

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.