views:

189

answers:

3

I am trying to define a generic container whose elements can return the enclosing container. Something like:

abstract class Container[E <: Element] { // compile error
  def contains( e: E ): Boolean
  def addNewElement(): Unit
}

abstract class Element[C <: Container] { // compile error
  def enclosingContainer(): C
}

class MyContainer extends Container[MyElement] {
  private var elements = List[MyElement]()
  override def contains( elem: MyElement ) = elements.contains( elem )
  override def addNewElement() { elements ::= new MyElement(this) }
}

class MyElement( container: MyContainer ) extends Element[MyContainer] {
  override val enclosingContainer = container
}

However that snippet does not compile because I should give a type parameter to Element in the abstract class Container[E <: Element] definition and a type to Container in the abstract class Element[C <: Container] definition.

I there a way to achieve the behavior I am looking for ? Is there an appropriate declaration for Container and Element? Should I define a third-party object ?

+6  A: 
abstract class Container[E <: Element[_]] {
  def contains( e: E ): Boolean
  def addNewElement(): Unit
}

abstract class Element[C <: Container[_]] {
  def enclosingContainer(): C
}

class MyContainer extends Container[MyElement] {
  private var elements = List[MyElement]()
  override def contains( elem: MyElement ) = elements.contains( elem )
  override def addNewElement() { elements ::= new MyElement(this) }
}

class MyElement( container: MyContainer ) extends Element[MyContainer] {
  override val enclosingContainer = container
}
Daniel
+5  A: 

Using type members instead of type parameters would avoid the issue:

abstract class Container { // compile error
  type E <: Element
  def contains( e: E ): Boolean
  def addNewElement(): Unit
}

abstract class Element { // compile error
  type C <: Container
  def enclosingContainer(): C
}

class MyContainer extends Container {
  type E = MyElement
  private var elements = List[MyElement]()
  override def contains( elem: MyElement ) = elements.contains( elem )
  override def addNewElement() { elements ::= new MyElement(this) }
}

class MyElement( container: MyContainer ) extends Element {
  type C = MyContainer
  override val enclosingContainer = container
}
Mitch Blevins
+5  A: 

The other solutions already given fail to enforce that the types match: that is, given a type ContainerImpl extends Container, you should be sure that ContainerImpl.E.C should be ContainerImpl and not some other container. Here is one which does enforce this (adapted from http://programming-scala.labs.oreilly.com/ch13.html):

abstract class ContainerWithElement {
  type C <: Container
  type E <: Element

  trait Container {
    self: C =>
    def contains( e: E ): Boolean
    def addNewElement(): Unit
  }

  trait Element {
    self: E =>
    def enclosingContainer(): C
  }
}
Alexey Romanov