首页 > 解决方案 > Scala Variance Concept,为什么它不能编译

问题描述

我是 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) 行不编译?对我来说,它们都与序列相同,是列表的子类型。如何让它发挥作用?就像1号线一样?

https://docs.scala-lang.org/tutorials/FAQ/collections.html [列出对象层次结构]

标签: scalaconcept

解决方案


你看到的叫做逆变。函数参数需要是逆变的,因为:

class HisSubClass extends MyClass

val his = new HisSubClass 
f7(his) // his is accepted as MyClass

Nowf4将被调用的东西不是MySubClass,这将是错误的。

Seq/的情况是List有效的,因为它是相反的。List是 的子类Seq

val af2: (List[_]) => Boolean = af0

就好像

val aff0: (MyClass) => Boolean = (s) ⇒ { s eq null }
val aff2: (MySubClass) => Boolean = aff0

承诺(合同)

对我理解参数/返回值差异有很大帮助的是将类型声明视为承诺(或合同)。返回值是协变的,因为您已承诺您的返回值将是 type MyClass,并且通过MySubClass在子类中提供您仍然信守诺言。承诺您将接受类型的参数,MyClass然后尝试声明子类成员接受仅MySubClass意味着尝试缩小承诺范围,这是您不能做到的(子类必须完全实现父类)。

在您的示例中,f4您已承诺该函数将MySubClass作为参数给出。当您尝试将其分配给您时,f7您正在尝试打破此承诺,因为您可以通过调用它来传递任何MyClassto 。f4f7


推荐阅读