Question

Je ne parviens pas à étendre une classe de base qui s'étend à Ordered [Base]. Ma classe dérivée ne peut pas étendre Ordered [Derived] et ne peut donc pas être utilisée comme clé dans un TreeMap. Si je crée un TreeMap [Base] puis substitue simplement la comparaison dans Derived, cela fonctionne mais ce n'est pas ce que je veux. J'aimerais pouvoir avoir la classe dérivée comme clé. Y a-t-il un moyen de contourner cela?

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
}

Modifier

Il semble que cette discussion sur la liste de diffusion scala être très pertinent, mais il me perd un peu.

Était-ce utile?

La solution

Vous pouvez utiliser une conversion de type de B en Commandé [B]:

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

Je pense que B doit toujours être un sous-type de A, ce qui implique que l'ordre [A] le type A est invariant. Il ne peut pas définir de seconde méthode de comparaison pour mettre en œuvre Order [B] avec le même type d'erreur que la méthode de comparaison de Ordered [A].

Vous pouvez également définir une version de type implicite de B à Ordered [B]:

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

Cela devrait être valide. Je ne suis pas au courant d'un moyen d'exprimer cela dans le système de typage sans transtypes.

Autres conseils

Le trait Commandé prend un paramètre. Un paramètre de type, accordé, mais qui fonctionne comme n'importe quel autre paramètre. Lorsque vous l'étendez deux fois, dans la classe de base et dans la sous-classe, vous n'utilisez pas "l'importation". deux versions de Ordered . Au lieu de cela, la linéarisation des classes a lieu et vous ne l'importez qu'une fois. Pour cette raison, vous ne pouvez pas lui transmettre deux paramètres différents.

Maintenant, il y a une raison pour laquelle TreeMap ne nécessite pas de sous-classe de Ordered , mais simplement une conversion de votre classe en Commandé . C'est précisément pour rendre de telles choses possibles. Au lieu d’étendre ces choses directement, vous devriez les impliquer:

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()

Vous pouvez mettre un objet implicite Ordering [B] quelque part, comme ceci:

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

EDIT : ceci n'est disponible que dans Scala 2.8 (merci, Ken)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top