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

Contravariant why Foo[C] is a subtype of Foo[B]?

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

• A type with an unannotated parameter Foo[A] is invariant in A. This means there is no relationship between Foo[B] and Foo[C] no matter what the sub- or super-type relationship is between B and C.

• A type with a parameter Foo[+A] is covariant in A. If C is a subtype of B, Foo[C] is a subtype of Foo[B].

• A type with a parameter Foo[-A] is contravariant in A. If C is a supertype of B, Foo[C] is a subtype of 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 的子类型?

C is a supertype B but why C change suddenly to subtype of B in contravariant?

这是逆变的定义,它反转关系顺序(在我们的例子中,"is subtype of"关系<:)。 请注意,并不是 C 现在是 B 的子类型,这种关系是固定的,它是 [=13= 的容器的东西],即 Foo[C],现在是 BFoo[B] 容器的子类型,而不是直接 B 本身。

逆变的经典例子是函数对象。 Scala 中的函数在参数类型上是逆变的,在 return 类型上是协变的,即 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())
}

为什么编译?因为当你用 Lion 调用 lionToString 时,你肯定知道它将能够调用在 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.
}

然后当我们的函数实际需要 Lion.

时,我们就可以传递 Animal 的不同子类型