有关Scala中协方差的一些问题

时间:2019-01-09 11:30:41

标签: scala covariance

我试图了解Scala中的协方差,但是找不到任何可以帮助我解决此问题的示例。 我有以下代码:

    class GenericCellImm[+T] (val x: T) {}

它编译良好,但是当我使用它

    class GenericCellMut[+T] (var x: T) { }

它不能编译。为什么在编写此代码时不能使用var(但可以使用val)?我该如何解决? 也是这种情况

    abstract class Sequence[+A] {
    def append(x: Sequence[A]): Sequence[A]} 

出什么问题了?

2 个答案:

答案 0 :(得分:9)

苹果是水果。

一袋不变的苹果是一袋不变的水果。一袋苹果里装着水果。您可以从这样的袋子中取出一个苹果(嗯,不是真的,因为它是一成不变的,但是您可以检索到苹果的副本),最后手里拿着水果。没问题。

但是,一袋易变的苹果不是一袋易变的水果,因为您可以放入一袋易变的水果。例如,像香蕉。一袋苹果只能容纳苹果,而香蕉不能容纳!

这正是Scala不允许使用第一个构造的原因。

第二个呢? Scala允许您做一些修改。但是结果是相反的。实际上,您可以创建X[Fruit]的一种类型X[Apple]X有点像书包,但它的作用方向相反。如果我们坚持水果类比,X可能是什么?认为它是榨汁机。您可以将苹果放入榨汁机中。您可以将任何种类的水果放入果汁机。有点矛盾的是,果汁榨汁机是一种苹果榨汁机!苹果榨汁机唯一能做的就是榨苹果。但是,榨汁机也可以做到这一点,这是一种(子类型)关系的基础。

答案 1 :(得分:5)

您不能写def append(x: Sequence[A]): Sequence[A]},因为A在append自变量中处于协变位置,而在协变中。

您应该这样写:

abstract class Sequence[+A] {
   def append[B >: A](x: Sequence[B]): Sequence[B]
}

您在Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work?

中有很好的解释

简而言之:

+ A表示在所有情况下将A转换为A的超类型都是安全的(狗可能会转换为Animal)。如果编写append[A](x: Sequence[A]),则表示x只能是A或A的子类型(狗,yorsay等),而不能是超类型(如Animal),因此这与+ A注释矛盾,并且在编译时失败。使用签名append[B >: A](x: Sequence[B])可以避免在函数参数中命名A来解决此问题。

因此,[B >: A]定义了B的下限,指出B必须是A或A的超类型,但绝不能是层次结构中低于A的任何类,因此符合+ A签名。 / p>

我知道协方差和协方差是复杂的概念并且很难理解,我有时也会感到困惑。