Scala编译器共同和反向方差规则

时间:2013-09-22 09:39:04

标签: scala covariance type-safety

我使用本书详尽地研究了使用共方差和反方差的编译规则:http://www.cs.ucsb.edu/~benh/260/Programming-in-Scala.pdf(p 423-424)

我想出了一个不应该根据规则编译的例子,尝试过它,实际上,由于协方差/反差方法问题,它不能编译。但是,我没有看到任何合理的原因,为什么它不应该工作。因此,与Java中的Array相反,我认为,即使编译它也无法实现任何非类型安全的行为。

class TypeTest[+U] {
  def call(func: () => U): U = func()
}

object Main extends App {
    val test: TypeTest[Number] = new TypeTest[Integer]
    test.call(() => 3)
}

编译输出是:

Main.scala:2: error: covariant type U occurs in contravariant position in type () => U of value func
  def call(func: () => U): U = func()

你能告诉我这个吗?

1 个答案:

答案 0 :(得分:3)

此定义:class TypeTest[+U]表示,例如TypeTest[Integer]TypeTest[Number]的有效替代。因此,根据Liskov substitution principleTypeTest[Number]可以做的任何事情,TypeTest[Integer]也必须能够做到。但这显然不是这种情况:如果您的代码已编译,则可以向期望至少Number的实例提供任何Integer

虽然一般理解这些问题可能很棘手,但您可以信任编译器的共同/逆变问题。这是因为我们很难让编译器执行这些额外的检查。但是如果你确实想要绕过差异检查,你可以做到这一点:

class TypeTest[+U] {
  def call(func: Function0[U @uncheckedVariance]): U = func()
}

使用此功能的原因示例

因为这会在运行时编译并抛出:

trait Fruit
case class Orange(orangeKind: String) extends Fruit
case class Apple(appleKind: String) extends Fruit

def main(args: Array[String]) {
  val testApple = new TypeTest[Apple] {
    override def call(func: () => Apple) = func().copy(appleKind = "Different")
  }
  val testFruit: TypeTest[Fruit] = testApple

  testFruit.call(() => Orange("Round"))
}