Scala Variance Concept,为什么它不编译

Scala Variance Concept, why it doesn't compile

我是 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 [列出对象层次结构]

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

class HisSubClass extends MyClass

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

现在 f4 会用不是 MySubClass 的东西调用,这会出错。

Seq / List 的情况是可行的,因为它是相反的。 ListSeq 的子 class。

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

就像

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

承诺(合同)

对理解参数/return 值差异有很大帮助的是将类型声明视为承诺(或合同)。 Return 值是协变的,因为你已经承诺你的 return 值将是 MyClass 类型,并且通过在子 class 中提供 MySubClass 你仍然保持你的承诺。承诺您将接受 MyClass 类型的参数,然后尝试声明一个仅接受 MySubClass 的 subclass 成员意味着试图缩小承诺范围,您不能这样做(subclass 必须完全实现父 class).

对于 f4 中的示例,您已承诺该函数将 MySubClass 作为参数提供。当您尝试将其分配给 f7 时,您试图打破这个承诺,因为您可以通过 f7.[=30= 调用它来将任何 MyClass 传递给 f4 ]

因为你试图给 Function1[MyClass, Boolean] 类型的值赋值 Function1[MyClass, Boolean] 类型的值,但是 Function1 的第一个类型参数是逆变的,见 API doc:

trait Function1[-T1, +R] extends AnyRef

但它允许您这样做:

val f7: MyClass => Boolean = s => s eq null 
val f44: (MySubClass) => Boolean = f7

您可以找到差异的解释 here