我使用泛型相当长的时间,但我从未使用过像List<? super T>
.
这是什么意思?如何使用它?擦除后的效果如何?
我还想知道:它是泛型编程(模板编程?)中的标准,还是只是 java 的“发明”?例如,c# 是否允许类似的构造?
我使用泛型相当长的时间,但我从未使用过像List<? super T>
.
这是什么意思?如何使用它?擦除后的效果如何?
我还想知道:它是泛型编程(模板编程?)中的标准,还是只是 java 的“发明”?例如,c# 是否允许类似的构造?
当您想将一个集合中的项目消费到另一个集合中时,使用此构造。例如,您有一个泛型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条。
在类型论中,这些东西被称为方差,<? extends T>
是协变符号,也是<? super T>
逆变符号。最简单的解释是它?
可以被任何T
在协变符号中扩展的?
类型所代替,并且可以被任何在逆变符号中扩展的类型所代替T
。
使用 co 和 contra-variance 比最初看起来要困难得多,特别是因为方差会根据位置“切换”。
一个简单的例子是函数类。假设您有一个接受 aA
并返回 a的函数B
。它的正确表示法是说它A
是逆变和B
os 协变的。为了更好地理解这是怎么回事,让我们考虑一个方法——我们称之为它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 无论如何都已经很生疏了,但我会在以后看看我能做些什么——如果没有人愿意先做的话。
这称为“有界通配符”。官方教程中解释的很好。
如教程中所述,您因此知道该列表包含的对象恰好是一个子类型T
例如List<? extends Number>
可以只保存Integer
s 或只保存Long
s,但不能同时保存。
Java 泛型常见问题解答对 Java 泛型有很好的解释。检查问题什么是有界通配符?它详细解释了构造“?super T”的用法。