我试图理解差异,在我买的书中解释如下:
•具有未注释参数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
子类型?
答案 0 :(得分:4)
C
是超类型B
,但为什么C
突然变为B
的子类型 逆变
这是逆变的定义,它颠倒关系顺序(在我们的例子中,&#34;是&#34;关系<:
的子类型)。
请注意,C
现在不是B
的子类型,该关系是固定的,是 C
的容器,即Foo[C]
,现在是B
,Foo[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
的不同子类型。