2

我使用本书详尽地研究了使用协方差和反方差的编译规则: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()

你能跟我说清楚吗?

4

1 回答 1

3

这个定义:class TypeTest[+U]意味着,例如, aTypeTest[Integer]是 a 的有效替代品TypeTest[Number]。因此,根据Liskov 替换原则,aTypeTest[Number]可以做的任何事情,aTypeTest[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"))
}
于 2013-09-22T09:49:06.253 回答