【问题标题】:Error with definition covariant and contravariant types定义协变和逆变类型的错误
【发布时间】:2013-01-11 12:27:12
【问题描述】:

我有代码:

class A {
    override def toString = "object class A"
}

class B extends A {
    override def toString = "object class B"
}

class Cell[+T](init: T) {
    private[this] var current: T = init
    def get: T = current
    def set(x: T) { current = x }
}

val cB = new Cell[B](new B)
println(cB.get)
val cA: Cell[A] = cB
println(cA.get)

但我有错误:def set(x: T) { current = x }

错误:协变类型 T 出现在类型 T 的逆变位置 值 x def set(x: T) { current = x }

请解释

【问题讨论】:

  • 除了有见地的答案之外,为什么还需要显式的私有引用和 getter/setter? Scala 为您做到这一点:class Cell[+T](val t: T) {...}

标签: scala


【解决方案1】:

类型的逆变位置是(除其他外)允许您将该类型的实例传递给方法的任何位置。所以所有方法参数类型都处于逆变位置。由于您将 T 声明为协变 (+T),因此编译器将不允许这样做。您唯一的选择是:

  • 使T 不变
  • 修改 set 方法,使其返回 Cell 的新实例,Cell 因而变得不可变。
  • 删除 set 方法,同时使 Cell 不可变

如果编译器允许您在实现时使用 set 方法,那会使类型系统不安全,因为它允许您编写:

val cs:Cell[String] = new Cell("")
val ca:Cell[Any] = cs
ca.set(5)
val s:String = cs.get //boom

【讨论】:

  • 应该是ca.set(5) 而不是cs.set(5)
【解决方案2】:

简短的回答是,您不能拥有一个协变的可变容器。假设这是编译的,那么在你的例子中,最后两行可能是:

val cA: Call[A] = cB
cA.set(new A)

第一行是允许的,因为Cell[B] 是 Cell[A],因为使用了+T。然后第二行也被允许 - 因为当然你可以设置一个Cell[A] 来保存一个A。

但是现在,cB.get 返回一个A 的实例,而不是B!这没有任何意义,也没有办法解决这个矛盾,同时仍然允许 Cell 的类型参数中存在协变。


如需更长、更正式/更全面的答案,请参阅此问题:Scala covariance/contravariance

【讨论】:

    猜你喜欢
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多