Étendre la classe scala qui s'étend ordonnée
-
10-07-2019 - |
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.
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)