我是Scala的菜鸟,所以请不要投反对票。
class MyClass extends AnyRef
class MySubClass extends MyClass
val af0: (Seq[_]) => Boolean = (s) ⇒ { s eq null }
val f4: (MySubClass) => Boolean = (s) => { s eq null }
val af2: (List[_]) => Boolean = af0 //(Line 1)
val f7: MyClass => Boolean = f4 //(Line 2)
为什么第(1)行编译而第(2)行不编译?对我来说,它们都与Sequence是List的子类型相同。如何使其运作?像第1行一样?
https://docs.scala-lang.org/tutorials/FAQ/collections.html [列表对象层次结构]
答案 0 :(得分:3)
您看到的称为contravariance。由于以下原因,函数参数必须是互变的:
class HisSubClass extends MyClass
val his = new HisSubClass
f7(his) // his is accepted as MyClass
现在f4
会被调用,而不是MySubClass
,这会出错。
使用Seq
/ List
的情况是可行的,因为反之亦然。 List
是Seq
的子类。
val af2: (List[_]) => Boolean = af0
就像
val aff0: (MyClass) => Boolean = (s) ⇒ { s eq null }
val aff2: (MySubClass) => Boolean = aff0
帮助我理解参数/返回值差异的很多方法是将类型声明视为承诺(或合同)。返回值是协变的,因为您已承诺您的返回值将为MyClass
类型,并且通过在子类中提供MySubClass
仍会遵守您的承诺。保证您将接受类型为MyClass
的参数,然后尝试声明仅接受MySubClass
的子类成员意味着试图缩小承诺,而您不能这样做(子类必须完全实现父类)。
以您在f4
中的示例为例,您已承诺将以函数MySubClass
作为参数。当您尝试将其分配给f7
时,您正试图违反此承诺,因为您可以通过MyClass
对其进行调用,从而将f4
传递给f7
。
答案 1 :(得分:2)