所有 在阅读了Daniel Spiewak提供的Scala covariance / contravariance question及其答案以及“Scala编程”一书的第19.3-19.7节后,我对Function1 [-A,+ B]的定义产生了另一种疑惑:为什么它的第一个类型参数逆变?我有一个原因,就是这个类型参数保证当有多种情况时,子类型总是出现在超类型之前,同时,子类型“是一个”超类型。例如:
class A
class B extends A
class C extends B
scala> val withDefault: A => B = {
| case x:B => new B
| case x:A => new B }
withDefault: A => B = <function1>
这里,(1)case x:B
早于case x:A
,(2)功能1 [A,B]&lt ;: Function1 [B,B]
还有其他原因吗?任何信息都将不胜感激!
答案 0 :(得分:9)
逆变法与模式匹配无关。这就是为什么函数类型在它们的参数类型上是逆变的。考虑这个功能:
def doHigherOrder(handleAnyAnimal: Animal => T,
anyAnimal: Animal ): T = {
// ...foo...
handleAnyAnimal(anyAnimal)
// ...bar...
}
如果函数是协变而不是逆变,那么函数Duck => T
也可以是Animal => T
的子类型,你可以doHigherOrder(handleAnyDuck)
。这将是一个错误,因为在doHigherOrder
内,handleAnyAnimal(anyAnimal)
表达式(在运行时/评估时)会归结为handleAnyDuck(anyAnimal)
,这显然是不对的,因为一个函数可以处理任何Duck
可能无法处理任何Animal
:
def doHigherOrder(handleAnyDuck: Duck => T,
anyAnimal: Animal ): T = {
// ...foo...
handleAnyDuck(anyAnimal) // <-- ERROR
// ...bar...
}
此外,假设Creature >: Animal
,函数handleAnyCreature: Creature => T
确实也是Animal => T
的子类型,因为将anyAnimal
传递给可以接受{{1}的内容是可以的}}
这就是为什么逆变在参数类型中直观上是不可避免的。
但是,返回值是协变的,因为它们与语义具有完全相同的语义:
anyCreature
与
比较val animal = getDuck() // ok
val duck = getAnimal() // <-- ERROR
形象地说,您将分配给函数参数,但从分配它们的返回值。使用命名参数变得更加明显:
val x = handleDuck(animal) // <-- ERROR
val y = handleAnimal(duck) // ok