逆变为什么Foo [C]是Foo [B]的子类型?

时间:2017-04-28 07:27:46

标签: scala contravariance

我试图理解差异,在我买的书中解释如下:

  

•具有未注释参数Foo [A]的类型在A中是不变的   意味着Foo [B]和Foo [C]之间没有任何关系   B和C之间的子类型或超类型关系是什么。

     

•具有参数Foo [+ A]的类型在A中是协变的。如果C是a   B的子类型,Foo [C]是Foo [B]的子类型。

     

•具有参数Foo [-A]的类型在A中是逆变的。如果C是a   B的超类型,Foo [C]是Foo [B]的子类型。

我无法理解这句话:

If C is a supertype of B, Foo[C] is a subtype of Foo[B].

为什么不:

Foo[C] is a supertype of Foo[B].

C是超类型B,但为什么C突然变为逆变的B子类型?

1 个答案:

答案 0 :(得分:4)

  

C是超类型B,但为什么C突然变为B的子类型   逆变

这是逆变的定义,它颠倒关系顺序(在我们的例子中,&#34;是&#34;关系<:的子类型)。 请注意,C现在不是B的子类型,该关系是固定的, C的容器,即Foo[C],现在是BFoo[B]容器的子类型,而不是直接B本身。

逆变的经典例子是函数对象。 Scala中的函数在其参数类型中是逆变的,并且在它们的返回类型中是协变的,即Function1[-T, +R]

让我们看一个例子。假设我们有一个小的ADT:

sealed trait Animal
case class Mouse() extends Animal
case class Lion() extends Animal

现在我们想要从Lion => String创建一个函数。我们可以从Animal => String

提供具体功能吗?
def main(args: Array[String]): Unit = {
  val animalToString: (Animal) => String = an => an.toString
  val lionToString: (Lion) => String = animalToString

  lionToString(new Lion())
}

为什么这会编译?因为当您使用lionToString调用Lion时,您肯定知道它可以调用Animal上定义的任何函数,因为Lion <: Animal。但另一种方式并非如此。假设Function1在其参数类型中是协变的:

def main(args: Array[String]): Unit = {
  val lionToString: (Lion) => String = an => an.toString
  val animalToString: (Animal) => String = lionToString

  lionToString(new Mouse()) // <-- This would blow up.
}

然后,当我们的函数实际需要Animal时,我们就可以传递Lion的不同子类型。