3

在 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 具有以下签名:

trait Function1[-T1, +R] extends AnyRef

T1(输入类型)是逆变的,而 R(输出类型)是协变的。

我的理解正确吗?如果是这样,为什么 PECS 没有应用于 Scala 的 Function1 特征?

4

2 回答 2

6

Joshua 是正确的,Scala 也是如此。事实上,Scala 强制执行此规则,因此您无需担心。Java,因为它没有方差注释并且必须在调用站点处理存在性,所以需要它们来指导程序员进行正确的设计。

Scala 在定义站点有方差注释,而 java 在调用站点有存在论。调用的输出是定义的输入,反之亦然。

AFunction1没有从它的参数中读取,也没有写入它的结果,这就是reduce在 Joshua 的例子中所做的。相反,您必须将其Function1 视为一个集合。

当您写入集合时,该集合是消费者。与函数类似:当您调用它时,您正在写入它。因此,正在写入的输入参数必须是逆变的。

同样,当您从集合中读取时,该集合就是生产者。您通过查看函数的结果来读取函数,因此输出参数必须是协变的。

如您所见,这正是Function1符号。

于 2012-07-01T02:35:27.437 回答
5

通配符func适用于传递给方法的类型。它们意味着任何 ArrayList 都可以作为输入传递ArrayList<Object>ArrayList<Number>ArrayList<Integer>可以作为输出传递。该方法本身不是通用的。

对于 scala Function1 类型,差异适用于类型,因此Function1[AnyRef, Integer]可以在Function[AnyRef, Number]需要 a 的地方使用 a 。

于 2012-06-30T10:33:06.517 回答