如何确定类型参数的方差?
受到 Scala 中协变和逆变的真实示例< /a> 我认为更好的问题是:
在设计库时,在确定类型参数应该是协变还是逆变时,您是否应该问自己一组特定的问题?或者你应该让一切保持不变,然后根据需要进行更改?
Inspired by Real-world examples of co- and contravariance in Scala I thought a better question would be:
When designing a library, are there a specific set of questions you should ask yourself when determining whether a type parameter should be covariant or contravariant? Or should you make everything invariant and then change as needed?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
嗯,很简单,有道理吗?想想里氏替换。
协方差
如果
A <: B
,则在需要C[B]
的地方传递C[A]
是否有意义?如果是这样,请将其设为C[+T]
。经典的例子是不可变的List
,其中List[A]
可以传递给任何需要List[B]
的东西,假设A
是B
的子类型。两个反例:
可变序列是不变的,因为否则可能会出现类型安全违规(事实上,Java 的协变体
Array
很容易受到此类问题的影响,这就是为什么它在 Scala 中是不变的)。不可变的 Set 是不变的,尽管它的方法与不可变的 Seq 的方法非常相似。区别在于
contains
,它在集合上是类型化的,而在序列上是非类型化的(即接受Any
)。因此,即使可以使其具有协变性,但对特定方法增加类型安全性的渴望导致选择不变性而不是协变性。反方差
如果
A <: B
,则在需要C[A]
的地方传递C[B]
是否有意义?如果是这样,请将其设为C[-T]
。典型的例子是Ordering
。虽然一些不相关的技术问题阻止了Ordering
逆变,但直观的是,任何可以订购A
超类的东西也可以订购A.由此可见,
Ordering[B]
对所有B
类型(A
的超类型)元素进行排序,可以传递给需要B
的元素。代码>排序[A]。虽然 Scala 的
Ordering
不是逆变,但 Scalaz 的 < a href="http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/Order.scala.html" rel="noreferrer">顺序 正如预期的那样是逆变的。 Scalaz 的另一个示例是其Equal特征。混合方差?
Scala 中最明显的混合方差示例是 Function1(以及 2、3 等)。它接收的参数是逆变的,返回的参数是协变的。但请注意,
Function1
用于很多闭包,并且在很多地方都使用了闭包,而这些地方通常是 Java 使用(或将使用)单一抽象方法类的地方。因此,如果您遇到 SAM 类适用的情况,那么这很可能是混合逆变和协方差的地方。
Well, simple, does it make sense? Think of Liskov substitution.
Co-variance
If
A <: B
, does it make sense to pass aC[A]
where aC[B]
is expected? If so, make itC[+T]
. The classic example is the immutableList
, where aList[A]
can be passed to anything expecting aList[B]
, assumingA
is a subtype ofB
.Two counter examples:
Mutable sequences are invariant, because it is possible to have type safety violations otherwise (in fact, Java's co-variant
Array
is vulnerable to just such things, which is why it is invariant in Scala).Immutable
Set
is invariant, even though its methods are very similar to those of an immutableSeq
. The difference lies withcontains
, which is typed on sets and untyped (ie, acceptAny
) on sequences. So, even though it would otherwise be possible to make it co-variant, the desire for an increased type safety on a particular method led to a choice of invariance over co-variance.Contra-variance
If
A <: B
, does it make sense to pass aC[B]
where aC[A]
is expected? If so, make itC[-T]
. The classic would-be example isOrdering
. While some unrelated technical problems preventOrdering
from being contra-variant, it is intuitive that anything that can order a super-class ofA
can also orderA
. It follows thatOrdering[B]
, which orders all elements of typeB
, a supertype ofA
, can be passed to something expecting anOrdering[A]
.While Scala's
Ordering
is not contra-variant, Scalaz's Order is contra-variant as expected. Another example from Scalaz is its Equal trait.Mixed Variance?
The most visible example of mixed variance in Scala is
Function1
(and 2, 3, etc). It is contra-variant in the parameter it receives, and co-variant in what it returns. Note, though, thatFunction1
is what is used for a lot of closures, and closures are used in a lot of places, and these places are usually where Java uses (or would use) Single Abstract Method classes.So, if you have a situation where a SAM class applies, that's likely a place for mixed contra-variance and co-variance.