19

我使用泛型相当长的时间,但我从未使用过像List<? super T>.

这是什么意思?如何使用它?擦除后的效果如何?

我还想知道:它是泛型编程(模板编程?)中的标准,还是只是 java 的“发明”?例如,c# 是否允许类似的构造?

4

4 回答 4

11

当您想将一个集合中的项目消费到另一个集合中时,使用此构造。例如,您有一个泛型Stack,并且您想添加一个popAll以 Collection 作为参数的方法,并将堆栈中的所有项目弹出到其中。按照常识,这段代码应该是合法的:

Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ... ;
numberStack.popAll(objects);

但只有当你popAll这样定义时它才会编译:

// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
    while (!isEmpty())
    dst.add(pop());
}

硬币的另一面pushAll应该是这样定义的:

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    push(e);
}

更新: Josh Bloch 传播此助记符以帮助您记住要使用的通配符类型:

PECS代表生产者扩展,消费者超级

有关更多详细信息,请参阅Effective Java 第 2 版,第 28条。

于 2010-02-22T11:16:03.180 回答
4

在类型论中,这些东西被称为方差<? extends T>是协变符号,也是<? super T>逆变符号。最简单的解释是它?可以被任何T在协变符号中扩展的?类型所代替,并且可以被任何在逆变符号中扩展的类型所代替T

使用 co 和 contra-variance 比最初看起来要困难得多,特别是因为方差会根据位置“切换”。

一个简单的例子是函数类。假设您有一个接受 aA并返回 a的函数B。它的正确表示法是说它A是逆变和Bos 协变的。为了更好地理解这是怎么回事,让我们考虑一个方法——我们称之为它g——它接收这个假设的函数类,其中f应该接收 anArc2D并返回 a Shape

在内部g,这f称为传递 anArc2D并且返回值用于初始化 an Area(它需要 a Shape)。

现在,假设f您传递的 接收任何Shape并返回一个Rectangle2D. 既然 anArc2D也是 a Shape,那么g将 an 传递Arc2D给就不会出错f,并且由于 aRectangle2D也是 a Shape,那么它可以传递给Area的构造函数。

如果您尝试反转任何差异或在该示例中交换预期和实际类型,您会看到它失败。我现在没有时间写下这段代码,而且我的 Java 无论如何都已经很生疏了,但我会在以后看看我能做些什么——如果没有人愿意先做的话。

于 2010-02-22T11:42:06.340 回答
4

这称为“有界通配符”。官方教程中解释的很好。

如教程中所述,您因此知道该列表包含的对象恰好是一个子类型T

例如List<? extends Number>可以只保存Integers 或只保存Longs,但不能同时保存。

于 2010-02-22T11:18:57.310 回答
0

Java 泛型常见问题解答对 Java 泛型有很好的解释。检查问题什么是有界通配符?它详细解释了构造“?super T”的用法。

于 2010-02-22T12:22:51.490 回答