在Effective Java中,Joshua Bloch讨论了PECS(Producer-Extends,Consumer-Super)的原理。
我对此的理解是,为了提高API灵活性,输入(产生的集合)应该是协变的,输出(消耗的集合)应该是逆变的。
实现此原则的函数可以具有以下签名:
private static void func( ArrayList<? extends Object> input, ArrayList<? super Integer> output)
但是,在Scala中,Function1特征具有以下签名:
trait Function1[-T1, +R] extends AnyRef
T1(输入类型)是逆变的,而R(输出类型)是协变的。
我的理解是否正确?如果是这样,为什么PECS不适用于Scala的Function1特性?
答案 0 :(得分:6)
约书亚是对的,斯卡拉也是如此。实际上,Scala强制要求这个规则,所以你不必担心它。 Java,因为它没有方差注释,必须在调用站点处理存在感,需要它们指导程序员进行正确的设计。
Scala在定义网站上有方差注释,而java在调用网站上有存在感。调用的输出是定义的输入,反之亦然。
Function1
未读取其参数或写入其结果,这是reduce
在Joshua的示例中所做的事情。相反,您必须将Function1
视为集合。
当您写入集合时,该集合是消费者。与函数类似:当你调用它时,你正在写它。因此,正在写入的输入参数必须是反变量的。
同样,当您从集合中读取时,该集合是生产者。您通过查看其结果来读取函数,因此输出参数必须是共变量。
如您所见,这正是Function1
符号。
答案 1 :(得分:5)
func
上的通配符适用于传递给方法的类型。它们意味着可以传递任何ArrayList以进行输入,并且可以传递ArrayList<Object>
,ArrayList<Number>
或ArrayList<Integer>
以进行输出。该方法本身并不通用。
使用scala Function1类型时,方差适用于类型,因此Function1[AnyRef, Integer]
可用于需要Function[AnyRef, Number]
的位置。