泛型<!-的真实示例是什么?超级T->?

时间:2018-09-05 13:06:27

标签: java generics inheritance super

我知道<? 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));
  }
}

我正在寻找一个实际用例的示例,该示例可以在其中使用,而不是在解释它是什么。

9 个答案:

答案 0 :(得分:46)

我能想到的最简单的例子是:

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

取自同一Collections。这样,Dog可以实现Comparable<Animal>,并且如果Animal已经实现了该功能,则Dog无需执行任何操作。

编辑真实示例:

在进行一些电子邮件乒乓球之后,我被允许在我的工作场所中展示一个真实的例子(是的!)。

我们有一个名为Sink的接口(无关紧要),其思想是积累。声明非常简单(简化):

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

很显然,有一个辅助方法,它使用List并将其元素消耗到Sink中(这有点复杂,但要使其简单):

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

这很简单吧?好吧...

我可以拥有一个List<String>,但是我想将其消耗到Sink<Object>上-这对我们来说是很常见的事情;但这会失败:

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

为此,我们需要将声明更改为:

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

答案 1 :(得分:14)

假设您具有以下类层次结构: 猫从哺乳动物继承而来,而哺乳动物又从动物那里继承。

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

这些呼叫有效:

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 

但是这些调用无效有效:

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

因此,方法签名只是确保您从更具体的类(在继承层次结构中较低)复制到更通用的类(在继承层次结构中较高),而不是相反。

答案 2 :(得分:5)

例如,查看Collections.addAll方法实现:

ec2:Describe*

这里,可以将元素插入元素类型为元素public static <T> boolean addAll(Collection<? super T> c, T... elements) { boolean result = false; for (T element : elements) result |= c.add(element); return result; } 的超类型的任何集合中。

没有下界通配符:

T

以下内容无效:

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

因为术语List<Number> nums = new ArrayList<>(); Collections.<Integer>addAll(nums , 1, 2, 3); Collection<T>更具限制性。


另一个示例:

Java中的

Predicate<T>接口,该接口在以下方法中使用Collection<? super T>通配符:

<? super T>

default Predicate<T> and(Predicate<? super T> other); default Predicate<T> or(Predicate<? super T> other); 允许链接更大范围的不同谓词,例如:

<? super T>

答案 3 :(得分:3)

假设您有一个方法:

passToConsumer(Consumer<? super SubType> consumer)

然后使用任何可能消耗Consumer的{​​{1}}调用此方法:

SubType

例如:

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

因此我可以将class Animal{} class Dog extends Animal{ void putInto(List<? super Dog> list) { list.add(this); } } 放入DogList<Animal>中:

List<Dog>

如果将List<Animal> animals = new ArrayList<>(); List<Dog> dogs = new ArrayList<>(); Dog dog = new Dog(); dog.putInto(dogs); // OK dog.putInto(animals); // OK 的方法更改为putInto(List<? super Dog> list)

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>

putInto(List<Dog> list)

答案 4 :(得分:1)

我写了一个网络广播,所以有了类MetaInformationObject,它是PLS和M3U播放列表的超类。我进行了选择对话,所以我有:

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

此类具有方法public T getSelectedStream()
因此,呼叫者收到的T是具体类型(PLS或M3U),但需要在超类上工作,因此有一个列表:List<T super MetaInformationObject>。添加结果的地方。
这就是通用对话如何处理具体实现的方式,而其余代码则可以在超类上工作。
希望可以使它更加清晰。

答案 5 :(得分:1)

考虑以下简单示例:

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

希望这是一个令人信服的案例:您可以想象要为显示目的对数字进行排序(对于toString是合理的排序),但是Number本身并不具有可比性。

之所以编译,是因为integers::sort采用了Comparator<? super E>。如果仅使用Comparator<E>(在这种情况下,ENumber),则代码将无法编译,因为Comparator<Object>不是{{1}的子类型}(由于您的问题表明您已经理解的原因,因此我不再赘述)。

答案 6 :(得分:1)

这里的收藏就是一个很好的例子。

1中所述,List<? super T>可让您创建List来容纳类型比T派生少的元素,因此它可以容纳继承的元素来自T,类型为T,并且T继承自。

另一方面,List<? extends T>允许您定义一个List,它只能容纳从T继承的元素(在某些情况下甚至不是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));
  }
}

您要在此处将较少派生类型的List投影到较少派生类型的List。 这里List<? super T>向我们保证,src中的所有元素在新集合中均有效。

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

答案 7 :(得分:0)

说你有

Kunde

然后:

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

答案 8 :(得分:0)

为此,我想到了一些现实生活中的例子。我要提出的第一个想法是将现实世界的对象用于“即兴”功能。想象一下,您有一把套筒扳手:

public class SocketWrench <T extends Wrench>

套筒扳手的明显目的是用作Wrench。但是,如果您认为可以在钳子中用扳手敲打钉子,则可以具有如下所示的继承层次结构:

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

在这种情况下,即使将socketWrench.pound(Nail nail = new FinishingNail())视为非典型用法,您也可以致电SocketWrench

一直以来,如果SocketWrench被用作applyTorque(100).withRotation("clockwise").withSocketSize(14)而不是SocketWrench,则可以访问像Wrench这样的方法Hammer