通用 < ? super T > 的真实例子是什么?

我知道 <? super T>代表 T的任何超类(任何级别的 T的父类)。但是我真的很难想象这个通用绑定通配符的任何实际例子。

我明白 <? super T>的意思,我也见过这种方法:

public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}

我正在寻找一个例子的 现实生活中的用例这个建设可以使用,而不是为了解释它是什么。

6816 次浏览

Suppose you have a method:

passToConsumer(Consumer<? super SubType> consumer)

then you call this method with any Consumer which can consume SubType:

passToConsumer(Consumer<SuperType> superTypeConsumer)
passToConsumer(Consumer<SubType> subTypeConsumer)
passToConsumer(Consumer<Object> rootConsumer)

For exmaple:

class Animal{}


class Dog extends Animal{


void putInto(List<? super Dog> list) {
list.add(this);
}
}

So I can put the Dog into List<Animal> or List<Dog>:

List<Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();


Dog dog = new Dog();
dog.putInto(dogs);  // OK
dog.putInto(animals);   // OK

If you change putInto(List<? super Dog> list) method to putInto(List<Animal> list):

Dog dog = new Dog();


List<Dog> dogs = new ArrayList<>();
dog.putInto(dogs);  // compile error, List<Dog> is not sub type of List<Animal>

or putInto(List<Dog> list):

Dog dog = new Dog();


List<Animal> animals = new ArrayList<>();
dog.putInto(animals); // compile error, List<Animal> is not sub type of List<Dog>

I wrote a webradio, so I had the class MetaInformationObject, which was the superclass for PLS and M3U playlists. I had a selection dialogue, so I had:

public class SelectMultipleStreamDialog <T extends MetaInformationObject>
public class M3UInfo extends MetaInformationObject
public class PLSInfo extends MetaInformationObject

This class had a method public T getSelectedStream().
So the caller received a T which was of the concrete type (PLS or M3U), but needed to work on the superclass, so there was a list: List<T super MetaInformationObject>. where the result was added.
Thats how a generic dialogue could handle the concrete implementations and the rest of the code could work on the superclass.
Hope that makes it a little more clearer.

Suppose you have this class hierarchy: Cat inherits from Mammal, which in turn inherits from Animal.

List<Animal> animals = new ArrayList<>();
List<Mammal> mammals = new ArrayList<>();
List<Cat> cats = ...

These calls are valid:

Collections.copy(animals, mammals); // all mammals are animals
Collections.copy(mammals, cats);    // all cats are mammals
Collections.copy(animals, cats);    // all cats are animals
Collections.copy(cats, cats);       // all cats are cats

But these calls are not valid:

Collections.copy(mammals, animals); // not all animals are mammals
Collections.copy(cats, mammals);    // not all mammals are cats
Collections.copy(cats, animals);    // mot all animals are cats

So the method signature simply insures that you copy from a more specific (lower in the inheritance hierarchy) class to a more generic class (upper in the inheritance hierarchy), and not the other way round.

Say you have:

class T {}
class Decoder<T>
class Encoder<T>


byte[] encode(T object, Encoder<? super T> encoder);    // encode objects of type T
T decode(byte[] stream, Decoder<? extends T> decoder);  // decode a byte stream into a type T

And then:

class U extends T {}
Decoder<U> decoderOfU;
decode(stream, decoderOfU);     // you need something that can decode into T, I give you a decoder of U, you'll get U instances back


Encoder<Object> encoderOfObject;
encode(stream, encoderOfObject);// you need something that can encode T, I give you something that can encode all the way to java.lang.Object

The easiest example I can think of is:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}

taken from the same Collections. This way a Dog can implement Comparable<Animal> and if Animal already implements that, Dog does not have to do anything.

EDIT for a real example:

After some email ping-pongs, I am allowed to present a real example from my work-place (yay!).

We have an interface called Sink (it does not matter what it does), the idea is that is accumulates things. The declaration is pretty trivial (simplified):

interface Sink<T> {
void accumulate(T t);
}

Obviously there is a helper method that takes a List and drains it's elements to a Sink (it's a bit more complicated, but to make it simple):

public static <T> void drainToSink(List<T> collection, Sink<T> sink) {
collection.forEach(sink::accumulate);
}

This is simple right? Well...

I can have a List<String>, but I want to drain it to a Sink<Object> - this is a fairly common thing to do for us; but this will fail:

Sink<Object> sink = null;
List<String> strings = List.of("abc");
drainToSink(strings, sink);

For this to work we need to change the declaration to:

public static <T> void drainToSink(List<T> collection, Sink<? super T> sink) {
....
}

A few real life examples come to mind for this. The first one I like to bring up, is the idea of a real-world object being used for 'improvised' functionality. Imagine that you have a socket wrench:

public class SocketWrench <T extends Wrench>

The obvious purpose of a socket wrench it so be used as a Wrench. However, if you consider that a wrench could be used in a pinch to pound in a nail, you could have an inheritance hierarchy that looks like this:

public class SocketWrench <T extends Wrench>
public class Wrench extends Hammer

In this scenario, you would be able to call socketWrench.pound(Nail nail = new FinishingNail()), even though that would be considered an atypical use for a SocketWrench.

While all along, the SocketWrench would have access to be able to call methods like applyTorque(100).withRotation("clockwise").withSocketSize(14) if it's being used as a SocketWrench instead of just a Wrench, instead of a Hammer.

For example, look into the Collections.addAll method implmenetation:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
boolean result = false;
for (T element : elements)
result |= c.add(element);
return result;
}

Here, the elements can be inserted into any collection whose element type is a supertype of the type T of the element.

Without a lower bounded wildcard:

public static <T> boolean addAll(Collection<T> c, T... elements) { ... }

the following would have been invalid:

List<Number> nums = new ArrayList<>();
Collections.<Integer>addAll(nums , 1, 2, 3);

because the term Collection<T> is more restrictive than Collection<? super T>.


Another example:

Predicate<T> interface in Java, that uses a <? super T> wildcard in the following methods:

default Predicate<T> and(Predicate<? super T> other);


default Predicate<T>  or(Predicate<? super T> other);

<? super T> allows to chain a wider range of different predicates, for example:

Predicate<String> p1 = s -> s.equals("P");
Predicate<Object> p2 = o -> o.equals("P");


p1.and(p2).test("P"); // which wouldn't be possible with a Predicate<T> as a parameter

Consider this simple example:

List<Number> nums = Arrays.asList(3, 1.2, 4L);
Comparator<Object> numbersByDouble = Comparator.comparing(Object::toString);
nums.sort(numbersByDouble);

Hopefully this is a somewhat compelling case: You could imagine wanting to sort the numbers for display purposes (for which the toString is a reasonable ordering), but Number is not itself Comparable.

这是因为integers::sort采用了Comparator<? super E>。如果它只需要一个Comparator<E>(在这种情况下,ENumber),那么代码将无法编译,因为Comparator<Object>不是Comparator<Number>的子类型(由于您的问题表明您已经理解的原因,所以我不会深入讨论)。

Collections serve as a good example here.

As stated in 1, List<? super T> lets you create List that will hold elements of type, that are less derived than T, so it can hold elements that inherit from T, that are type of T and that T inherits from.

On the other hand, List<? extends T> lets you define a List that can hold only elements that inherit from T (in some cases not even of type T).

This is a good example:

public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}

Here you want to project List of less derived type to List of less derived type. Here List<? super T> assures us that all elements from src will be valid in the new collection.

1 : Difference between <? super T> and <? extends T> in Java