如何确定类型参数的变异性?

11

Scala中协变和逆变的实际例子启发,我想提出一个更好的问题:

在设计库时,当确定类型参数应该是协变还是逆变时,您应该问自己一组特定的问题吗?还是应该将所有内容都设置为不变,然后根据需要进行更改?

1个回答

16

简单来说,它有意义吗?想一下Liskov替换原则。

协变

如果 A <: B,那么将 C[A] 传递给期望的 C[B] 是否有意义?如果是这样,请将其改为 C[+T]。经典示例是不可变的 List,假设 AB 的子类型,则可以将 List[A] 传递给任何期望 List[B] 的方法。

两个反例:

可变序列是不变的,因为否则可能存在类型安全性问题(事实上,Java的协变 Array 存在此类问题,这就是为什么它在Scala中是不变的原因)。

不变的 Set 是不变的,即使它的方法与不可变的 Seq 非常相似。差异在于 contains,它在集合上进行了类型标记,并且在序列上未标记(即接受 Any)。因此,尽管否则可能使它成为协变,但对特定方法的增加类型安全性的渴望导致选择不变而不是协变。

逆变

如果 A <: B,那么将期望的 C[A] 传递给 C[B] 是否有意义?如果是这样,请将其改为 C[-T]。经典的例子是 Ordering。虽然一些不相关的技术问题阻止了 Ordering 成为逆变,但直观上认为可以对超类 A 进行排序的任何内容也可以对 A 进行排序。因此,Ordering[B] 可以对所有类型为 B 的元素进行排序,即 A 的超类型,可以传递给希望获得 Ordering[A] 的东西。

虽然Scala的 Ordering 不是逆变的,但ScalazOrder与预期相反。 Scalaz的另一个示例是其 Equal 特性。

混合变异?

在Scala中最明显的混合方差示例是Function1(以及2、3等)。它在接收的参数中是反变的,在返回的参数中是协变的。请注意,Function1用于许多闭包,并且闭包在许多地方使用,这些地方通常是Java使用(或将使用)单个抽象方法类的地方。
因此,如果您有一个适用于SAM类的情况,那么这可能是混合反变和协变的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接