Scala中具有存在类型的泛型

时间:2011-07-19 12:26:55

标签: generics scala

两个特性的故事看起来应该很好地一起玩但不要和我无法解释为什么这个代码不起作用或者编译错误真的试图告诉我。

所以......我们有

父母的特质......

trait PolyTreeHasParents[P <: PolyTreeHasChildren[_]]  { 


val _parents: ListBuffer[P] = ListBuffer()

def isRootNode = _parents.size == 0

def parents: List[P] = _parents.readOnly

def addParent(parent: P): PolyTreeHasParents[P] = {

    println(parent)

    if (parent == this)
        throw new IllegalArgumentException()

    _parents += parent

    // 

    this
}




} 

和儿童的特质......

trait PolyTreeHasChildren[C <: PolyTreeHasParents[_]]  {   


val _children: ListBuffer[C] = ListBuffer()

def isLeafNode = children == ListBuffer()

def children: List[C] = _children.readOnly

def += (child: C) : PolyTreeHasChildren[C] = {
    addChild(child)
}

def addChild(child: C): PolyTreeHasChildren[C] = {


    if (child == this)
        throw new IllegalArgumentException()

    _children += child

    child.addParent(this)  // <= ERROR HERE

    this

}

}

指向ERROR的指针指出找到的类型不匹配。

PolyTreeHasChildren.this.type(with underlying type PolyTreeHasChildren[C]) required: _$1 where type _$1 

我原以为添加

P :< PolyTreeHasParents[_]

会允许我添加对孩子父母的引用。

这是一个奇怪的部分......要审查,错误是:

required: _$1 where type _$1 

什么!

唉......我已经没有关于如何使这段代码发挥作用的想法:(

4 个答案:

答案 0 :(得分:3)

你可以通过两种方式避免这种明显的无限循环:

首先,删除不必要的类型边界(https://stackoverflow.com/questions/1332574/common-programming-mistakes-for-scala-developers-to-avoid/5602321#5602321) - 至少在您当前的PolyTreeHasParents实现中,没有必要说P必须是{{1}的子类型1}}。

其次,您可以向PolyTreeHasChildren添加另一个类型参数,指定实现类型,并将其用作自我类型。我认为这是集合库中的常见模式。

看起来像这样:

PolyTreeHasChildren

答案 1 :(得分:2)

错误信息似乎很奇怪,但这种疯狂方法有。 它的含义是:你传递的是某种已知类型的参数。但是你已经将它指定为某种未知类型的参数,在这种情况下,它被设置为_ $ 1。

在您的代码中,您可以完全摆脱类型参数:

trait PolyTreeHasParents {
  type P = PolyTreeHasChildren
  val _parents: ListBuffer[P] = ListBuffer()
  def isRootNode = _parents.size == 0
  def parents: List[P] = _parents.readOnly

  def addParent(parent: P): PolyTreeHasParents = {

    if (!_parents.contains(parent)) {
      println(parent)
      if (parent == this) throw new IllegalArgumentException()
      _parents += parent
      parent.addChild(this)
    }
    this
  }
}

trait PolyTreeHasChildren {
  type C = PolyTreeHasParents
  val _children: ListBuffer[C] = ListBuffer()

  def isLeafNode = children == ListBuffer()

  def children: List[C] = _children.readOnly

  def +=(child: C): PolyTreeHasChildren = {
    addChild(child)
  }

  def addChild(child: C): PolyTreeHasChildren = {
    if (!_children.contains(child)) {
      println(child)
      if (child == this)
        throw new IllegalArgumentException()
      _children += child
      child.addParent(this)
    }
    this
  }
}

看到此行为:

object Test {
  def main(args: Array[String]) {
    trait X extends PolyTreeHasParents with PolyTreeHasChildren
    trait Y extends PolyTreeHasParents with PolyTreeHasChildren
    val x0, x1, x2 = new  X {}
    val y0, y1, y2 = new  Y {}
    x0.addChild(x1)
    x1.addChild(x2)
    y2.addParent(y1)
    y1.addParent(y0)
    x0.addParent(y2)
  }
}

现在让我们将其与“n.m。”解决方案的行为进行比较:

object Test {
  def main(args: Array[String]) {
    trait X extends PolyTreeHasParents[X, X] with PolyTreeHasChildren[X, X]
    trait Y extends PolyTreeHasParents[Y, Y] with PolyTreeHasChildren[Y, Y]
    val x0, x1, x2 = new X {}
    val y0, y1, y2 = new Y {}
    x0.addChild(x1)
    x1.addChild(x2)
    y2.addParent(y1)
    y1.addParent(y0)
//    x0.addParent(y2) // will not compile
  }
}      

答案 2 :(得分:1)

你有没有试过像

这样的东西
 trait PolyTreeHasParents[P <: PolyTreeHasChildren[PolyTreeHasParents[P]]]  { }

 trait PolyTreeHasChildren[C <: PolyTreeHasParents[PolyTreeHasChildren[C]]]  { }

存在类型在这里并不好。你必须说“我的父母有一些不知名的孩子”。你应该说“我的父母有孩子,就像我一样”。

免责声明:我没有机会对此进行测试(附近没有真正的电脑,只有手机)。

更新:不,这不起作用。如果你仍然感兴趣,下面是一些工作代码。我已经添加了对称(addParent调用addChild,addChild调用addParent),以说明当真正需要循环依赖时它是如何工作的。我已经使用了0 __的想法,在那里注入sel-type。 import scala.collection.mutable.ListBuffer

trait PolyTreeHasParents[Repr <: PolyTreeHasParents[Repr, P], 
      P <: PolyTreeHasChildren[P, Repr]]  {   

me: Repr =>

    val _parents: ListBuffer[P] = ListBuffer()
    def isRootNode = _parents.size == 0
    def parents: List[P] = _parents.readOnly
    def addParent(parent: P): Repr = { 
        if (! _parents.contains(parent) {
            println(parent)
            if (parent == this)
            throw new IllegalArgumentException()
            _parents += parent
            parent.addChild(this)
        }
        this
    }
}

trait PolyTreeHasChildren[Repr <: PolyTreeHasChildren[Repr, C],
      C <: PolyTreeHasParents[C, Repr]]  {

me: Repr =>

    val _children: ListBuffer[C] = ListBuffer()
    def isLeafNode = children == ListBuffer()
    def children: List[C] = _children.readOnly
    def += (child: C) : PolyTreeHasChildren[Repr, C] = { 
        addChild(child)
    }
    def addChild(child: C): Repr = { 
        if (! _children.contains(child) {
            println(child)
            if (child == this)
              throw new IllegalArgumentException()
            _children += child
            child.addParent(this)
        }
        this
    }
}

// Usage example
class PP extends PolyTreeHasChildren[PP, CC]
class CC extends PolyTreeHasParents[CC, PP]

答案 3 :(得分:1)

类型参数边界的复杂性源于a.o.从您的PolyTree由2个特征组成的事实。一个包含父母,另一个包含孩子。

我不明白具有这些独立特征的用例可能是什么。因为在PolyTree中,通常所有节点都可以有子节点和/或父节点。所以我认为人们总是将它们混合在一起。

如果是这种情况,那么可以摆脱大部分类型参数边界的复杂性:

trait PolyTree[Self <: PolyTree[Self] ] {
  self: Self =>

  private val _parents: ListBuffer[Self] = ListBuffer()
  def isRootNode = _parents.isEmpty
  def parents: List[Self] = _parents.readOnly

  def addParent(parent: Self): Self = {

    if (!_parents.contains(parent)) {
      println(parent)
      if (parent == this) throw new IllegalArgumentException()
      _parents += parent
      parent.addChild(this)
    }
    this
  }

  private val _children: ListBuffer[Self] = ListBuffer()
  def isLeafNode = _children.isEmpty
  def children: List[Self] = _children.readOnly

  def addChild(child: Self): Self = {
    if (!_children.contains(child)) {
      println(child)
      if (child == this)
        throw new IllegalArgumentException()
      _children += child
      child.addParent(this)
    }
    this
  }

}

用例:

object UseCasePolyTree {
  trait X extends PolyTree[X]
  trait Y extends PolyTree[Y]
  val x0, x1, x2 = new X {}
  val y0, y1, y2 = new Y {}
  x0.addChild(x1)
  x1.addChild(x2)
  val xx1: X = x2.parents.head
  y2.addParent(y1)
  y1.addParent(y0)
//  x0.addParent(y2) // will not compile
}

除此之外:polytree是非循环的。您仍然必须添加代码以防止创建循环。