11

受到Scala中协变和逆变的真实示例的启发,我认为一个更好的问题是:

在设计库时,在确定类型参数应该是协变还是逆变时,是否应该问自己一组特定的问题?还是应该让所有内容保持不变,然后根据需要进行更改?

4

1 回答 1

16

嗯,很简单,有意义吗?想想 Liskov 换人。

协方差

如果,在预期a 的地方A <: B传递 a 是否有意义?如果是这样,做它。经典的例子是不可变的,其中 a可以传递给任何期望 a 的东西,假设是 的子类型。C[A]C[B]C[+T]ListList[A]List[B]AB

两个反例:

可变序列是不变的,因为否则可能会违反类型安全(事实上,Java 的协变体Array很容易受到这些事情的影响,这就是为什么它在 Scala 中是不变的)。

ImmutableSet是不变的,尽管它的方法与 immutable 的方法非常相似Seq。区别在于contains,它是在集合上输入的,而在序列上是无类型的(即,接受Any)。因此,即使有可能使其成为协变,但对特定方法的类型安全性增加的愿望导致选择不变而不是协变。

逆变换

如果,在预期a 的地方A <: B传递 a 是否有意义?如果是这样,做它。经典的可能示例是. 虽然一些不相关的技术问题阻止了逆变,但直觉上任何可以订购超类的东西也可以订购。因此,它可以将 type 的所有元素排序为 的超类型,可以传递给期望 a 的东西。C[B]C[A]C[-T]OrderingOrderingAAOrdering[B]BAOrdering[A]

虽然 ScalaOrdering不是逆变的,但 ScalazOrder是逆变的。Scalaz 的另一个例子是它的Equal特性。

混合方差?

Scala 中最明显的混合方差示例是Function1(和 2、3 等)。它接收的参数是逆变的,返回的参数是协变的。但是请注意,这Function1就是很多闭包所使用的,而且很多地方都使用了闭包,而这些地方通常是 Java 使用(或将使用)单一抽象方法类的地方。

因此,如果您遇到适用 SAM 类的情况,那么这很可能是混合逆变和协方差的地方。

于 2011-03-12T01:10:21.337 回答