受到Scala中协变和逆变的真实示例的启发,我认为一个更好的问题是:
在设计库时,在确定类型参数应该是协变还是逆变时,是否应该问自己一组特定的问题?还是应该让所有内容保持不变,然后根据需要进行更改?
受到Scala中协变和逆变的真实示例的启发,我认为一个更好的问题是:
在设计库时,在确定类型参数应该是协变还是逆变时,是否应该问自己一组特定的问题?还是应该让所有内容保持不变,然后根据需要进行更改?
嗯,很简单,有意义吗?想想 Liskov 换人。
如果,在预期a 的地方A <: B
传递 a 是否有意义?如果是这样,做它。经典的例子是不可变的,其中 a可以传递给任何期望 a 的东西,假设是 的子类型。C[A]
C[B]
C[+T]
List
List[A]
List[B]
A
B
两个反例:
可变序列是不变的,因为否则可能会违反类型安全(事实上,Java 的协变体Array
很容易受到这些事情的影响,这就是为什么它在 Scala 中是不变的)。
ImmutableSet
是不变的,尽管它的方法与 immutable 的方法非常相似Seq
。区别在于contains
,它是在集合上输入的,而在序列上是无类型的(即,接受Any
)。因此,即使有可能使其成为协变,但对特定方法的类型安全性增加的愿望导致选择不变而不是协变。
如果,在预期a 的地方A <: B
传递 a 是否有意义?如果是这样,做它。经典的可能示例是. 虽然一些不相关的技术问题阻止了逆变,但直觉上任何可以订购超类的东西也可以订购。因此,它可以将 type 的所有元素排序为 的超类型,可以传递给期望 a 的东西。C[B]
C[A]
C[-T]
Ordering
Ordering
A
A
Ordering[B]
B
A
Ordering[A]
虽然 ScalaOrdering
不是逆变的,但 Scalaz的Order是逆变的。Scalaz 的另一个例子是它的Equal特性。
Scala 中最明显的混合方差示例是Function1
(和 2、3 等)。它接收的参数是逆变的,返回的参数是协变的。但是请注意,这Function1
就是很多闭包所使用的,而且很多地方都使用了闭包,而这些地方通常是 Java 使用(或将使用)单一抽象方法类的地方。
因此,如果您遇到适用 SAM 类的情况,那么这很可能是混合逆变和协方差的地方。