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
的情况是可行的,因为它是相反的。 List
是 Seq
的子 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。
我是 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
的情况是可行的,因为它是相反的。 List
是 Seq
的子 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。