Pregunta

Tengo problemas para extender una clase base que extiende Ordered [Base]. Mi clase derivada no puede extender Ordered [Derived], por lo que no se puede usar como clave en un TreeMap. Si creo un TreeMap [Base] y luego anulo la comparación en Derivado, eso funciona pero no es lo que quiero. Me gustaría poder tener la clase derivada como clave. ¿Hay alguna forma de evitar esto?

case class A(x: Int) extends Ordered[A] {
  def compare(that: A) = x.compare(that.x)
}

// Won't compile
//  case class B(val y : Int) extends A(1) with Ordered[B] {
//    def compare(that: B) = x.compare(that.x) match {
//      case 0 => y.compare(that.y)
//      case res => res
//    }
//  }

// Compiles but can't be used to define a TreeMap key
case class B(y: Int) extends A(1) {
  override def compare(that: A) = that match {
    case b: B => x.compare(b.x) match {
      case 0 => y.compare(b.y)
      case res => res
    }
    case _: A => super.compare(that)
  }
}

def main(args: Array[String]) {
  TreeMap[B, Int]() // Won't compile
}

Editar

Esta discusión en la lista de correo scala parece ser muy relevante pero me pierde un poco.

¿Fue útil?

Solución

Puede usar una conversión de tipo de B a Ordenada [B]:

class OrderedB(me : B) extends Ordered[B]{
    def compare(that: B) = me compare that
}
collection.immutable.TreeMap.empty[B, Int](new OrderedB(_))

Creo que B siempre tiene que ser un subtipo de A, lo que implica que la Orden [A] cuyo tipo A es invariante. No puede definir un segundo método de comparación para implementar la Orden [B] con el mismo tipo de error que el método de comparación de la Orden [A].

Alternativamente, puede definir versiones de tipo implícito de B a Ordenado [B]:

implicit def orderedA2orderedB[B <: A with Ordered[A]](b : B) : Ordered[B] = b.asInstanceOf[Ordered[B]]
collection.immutable.TreeMap[B, Int]()

Esto debería ser válido. No conozco una forma de expresar esto en el sistema de tipos sin conversiones.

Otros consejos

El rasgo Ordenado toma un parámetro. Un parámetro de tipo, otorgado, pero funciona como cualquier otro parámetro. Cuando lo extiende dos veces, en la clase base y en la subclase, no está importando " dos versiones de Ordered . En cambio, se lleva a cabo la linealización de las clases, y solo se importa una vez. Por esa razón, no puede pasarle dos parámetros diferentes.

Ahora, hay una razón por la cual TreeMap no requiere una subclase de Ordenado , solo una conversión de su clase a un Ordenado de él. Es precisamente para hacer posibles tales cosas. En lugar de extender estas cosas directamente, debería implicarlas:

scala> class A(val x: Int)
defined class A

scala> class B(x : Int, val y : Int) extends A(x)
defined class B

scala> import scala.collection.immutable.TreeMap
import scala.collection.immutable.TreeMap

scala> class AOrd(a: A) extends Ordered[A] {
     |   def compare(that: A) = a.x.compare(that.x)
     | }
defined class AOrd

scala> object AOrd {
     | implicit def toAOrd(a: A) = new AOrd(a)
     | }
defined module AOrd

scala> class BOrd(b: B) extends Ordered[B] {
     |   def compare(that: B) = b.x.compare(that.x) match {
     |     case 0 => b.y.compare(that.y)
     |     case res => res
     |   }
     | }
defined class BOrd

scala> object BOrd {
     | implicit def toBOrd(b: B) = new BOrd(b)
     | }
defined module BOrd

scala> import AOrd._
import AOrd._

scala> import BOrd._
import BOrd._

scala> TreeMap[B, Int]()
res1: scala.collection.immutable.SortedMap[B,Int] = Map()

Podría poner un orden implícito [B] dentro del alcance en algún lugar, como este:

  object BOrdering extends Ordering[B] {
    def compare(a: B, b: B) = a.compare(b)
  }
  implicit val bo = BOrdering
  TreeMap[B, Int]() // Now it works!

EDITAR : Esto solo está en Scala 2.8 (gracias, Ken)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top