【问题标题】:How is Scala's Covariance, Contravariance useful?Scala 的协变、逆变有什么用?
【发布时间】:2014-05-14 10:50:35
【问题描述】:

我一直在学习 scala 对协变和逆变参数化类型的使用;下面的示例代码让我有些困惑:

class Thing
class Creature extends Thing
class Human extends Creature
class Dog extends Creature

class Home[T >: Creature] {
  private var inside = Set[T]()

  def enter(entering: T) = inside += entering

  def whoIsInside = inside
}

val house = new Home[Thing]
val cage = new Home[Creature]
house enter new Human
cage enter new Dog

据我了解,参数化类 Home 使用了 Creature 下限的逆变,所以

val flat = new Home[Human]

导致编译器错误,这是我所期望的。我的困境是,我创造了一个新的“房子”,但我可以在里面放一个“人”!虽然这也是有道理的,因为“人类”是“事物”,但我天真地期待它会失败!抛开机制不谈,协变、逆变有什么用?

【问题讨论】:

标签: scala covariance contravariance


【解决方案1】:

在您的示例中,您可以将 T 的子类型放入 T 的集合中,因为 U <: t>

当你的 House[U] 是 U <: t house>

例如,

class Home[+T]
val cage: Home[Creature] = new Home[Human]

这方面最有用的例子是当您将 Nil 用于 List 时,因为 Nil 是 List[Nothing] 而 List 是协变的。所以 List[Nothing] 可以完全替代任何类型的 List。

逆变是相反的。如果 U <: t house>

例如,

class Home[-T]
val cage: Home[Human] = new Home[Creature]

【讨论】:

  • 我注意到,如果我包含 Home 的原始主体,编译器会在 Home[+T] 和 Home[-T] 处犹豫。
【解决方案2】:

在回答问题之前,我需要注意两点:

a) 您在示例中没有使用协方差/逆变,您只是定义了一个下限

b) 在标题中,您暗示这是一个 Scala 概念。协变和逆变是面向对象中的通用概念,早在 Scala 之前就已经存在。

那么关于您最初的问题,它有什么用处?它允许您为参数化类型指定继承。例如,如果您将类型参数定义为协变,可能以 Creature 作为上限,您可以表示狗的家可以代替生物的家:

scala> class Home[+T <: Creature]
defined class Home

scala> var home = new Home[Creature]
home: Home[Creature] = Home@46a32efb

scala> home = new Home[Dog]
home: Home[Creature] = Home@1b955e70

所以 Home[Dog] 是 Home[Creature] 的子类型 - 协方差允许您表达这一点。

还请注意,在您的示例中,仅使类型参数协变不会编译,因为您允许输入。方法参数不能是协变的,因为这会破坏可替代性。 Scala 编译器会为您检测到这一点。

【讨论】:

    猜你喜欢
    • 2015-02-09
    • 2010-10-22
    • 1970-01-01
    • 2013-11-29
    • 1970-01-01
    • 2020-01-25
    • 2012-12-03
    • 2012-08-02
    • 1970-01-01
    相关资源
    最近更新 更多