Scala协方差错误

时间:2013-11-18 17:40:44

标签: scala covariance

我正在尝试编译以下代码,但最后一行无法编译:

class SuperContainer (
  val shapeSets: Set[MyContainer[Shape]] = Set.empty[MyContainer[Shape]]) {

  def addAct(el: MyContainer[Shape]) = {
    new SuperContainer(shapeSets + el)
  }

}

class MyContainer[A](val ls: Set[A] = Set.empty[A]) {
  def addElement(el: A) = {
    new MyContainer(ls + el)
  }
}  


abstract class Shape

case class Circle(radius: Int) extends Shape {
  override def toString = "Circle(" + radius + ")"
}
case class Square(s: Int) extends Shape {
  override def toString = "Square(" + s + ")"
}


object MyContainer {
  def main(args: Array[String]) {
    //Circle Container
    val myc1 = new MyContainer[Circle]()
    val myc11 = myc1.addElement(new Circle(6))

    //Square Container
    val myc2 = new MyContainer[Square]()
    val myc21 = myc2.addElement(new Square(6))


    val scont = new SuperContainer
    scont.addAct(myc11) //does not compile
  }
}

Scala编译器建议我在MyContainer类定义中使用+ A,但通过这样做,会发生其他编译错误。我做错了什么或者这只是Scala限制?有没有办法克服这个问题?

1 个答案:

答案 0 :(得分:3)

为了达到你想要的效果,MyContainer必须是协变的:

class MyContainer[+A](val ls: Set[A] = Set.empty[A]) // ...

现在,您对addElement的定义将导致错误,因为A出现在逆变位置(在这种情况下作为函数参数)。您必须按如下方式调整您的签名:

def addElement[B >: A](el: B): MyContainer[B]

如果您这么想,这是有道理的:如果您有一个Container[Circle](由于协方差可以被视为Container[Shape])并且您添加Shape,那么您有一个Container[Shape]最后Container[Circle]而不是addElement

Set[A]的实施不会改变。

此外,由于val is not covariant,您无法在课堂外提供Set[A](即您必须删除class MyContainer[+A](ls: Set[A] = Set.empty[A]) // ... )。如果要访问元素,则必须添加其他方法来查询集合。

Set[A]

<强>更新

这是为了更清楚地解释为什么MyContainer[+A]不能成为class A class B extends A 公共API的一部分。说我们有:

val x: MyContainer[A] = new MyContainer[B]

想象一下:

val s = x.ls // get internal set

由于协方差,我们可以做到这一点。但是,如果我们现在可以打电话:

s

我们希望Set[A]的类型为Set。但是,x的内部Set[B]Set[A] which is not a {{1}}。因此,输入不正确。