scala - 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 [列出对象层次结构]
解决方案
你看到的叫做逆变。函数参数需要是逆变的,因为:
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
您正在尝试打破此承诺,因为您可以通过调用它来传递任何MyClass
to 。f4
f7
推荐阅读
- scala - 相当于 scala 中的 assoc-in(clojure)
- jenkins-pipeline - 如何在管道中访问 Vault 凭据
- python - “sklearn.ensemble.GradientBoostingClassifier”的损失参数解释
- c# - 在 Unity 中启动 Android 电子邮件客户端,指定收件人、主题和正文作为 Intent Extras... 获取 NoSuchFieldError
- android - 如何创建一个 .pwd 文件来为 Bundle Tool 签名 APK 集?
- c++ - Visual Studio C++ 位域结构体大小问题
- sap - CRM_ORDER_READ FM 的标准数据源
- java - 如何将包含多个 JSON 的文本文件拆分为每个数组元素包含单个 json 的字符串数组?
- ios - 使用 CAShapeLayer 为蒙版的一部分设置透明度动画
- selenium - 如何使用 span 类从下拉列表中选择一个值。常规的下拉功能不适用于 span 类