Estendi la classe scala che estende ordinata
-
10-07-2019 - |
Domanda
Ho problemi a estendere una classe base che estende Ordered [Base]. La mia classe derivata non può estendere Ordered [Derived], quindi non può essere utilizzata come chiave in una TreeMap. Se creo una TreeMap [Base] e quindi eseguo l'override del confronto in Derived, questo funziona ma non è quello che voglio. Vorrei poter avere la classe derivata come chiave. C'è un modo per aggirare questo?
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
}
Modifica
Questa discussione sulla mailing list di scala sembra essere molto pertinente ma mi perde un po '.
Soluzione
Puoi utilizzare una conversione di tipo da B a Ordinato [B]:
class OrderedB(me : B) extends Ordered[B]{
def compare(that: B) = me compare that
}
collection.immutable.TreeMap.empty[B, Int](new OrderedB(_))
Penso che B debba sempre essere un sottotipo di A che implica l'ordine [A] che tipo A è invariante. Non può definire un secondo metodo di confronto per implementare Order [B] con lo stesso tipo di errore del metodo di confronto di Ordered [A].
In alternativa puoi definire un tipo implicito di versioni da B a Ordinato [B]:
implicit def orderedA2orderedB[B <: A with Ordered[A]](b : B) : Ordered[B] = b.asInstanceOf[Ordered[B]]
collection.immutable.TreeMap[B, Int]()
Questo dovrebbe essere valido. Non sono a conoscenza di un modo per esprimere questo nel sistema dei tipi senza cast.
Altri suggerimenti
Il tratto Ordinato
accetta un parametro. Un parametro di tipo, garantito, ma funziona esattamente come qualsiasi altro parametro. Quando lo estendi due volte, nella classe base e nella sottoclasse, non stai "importando" due versioni di ordinato
. Invece, la linearizzazione delle classi ha luogo e la importi una sola volta. Per tale motivo, non è possibile passarci due parametri diversi.
Ora, c'è un motivo per cui TreeMap
non richiede una Ordinato
, solo una conversione dalla tua classe in un
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()
Potresti mettere un Ordine implicito [B] in ambito da qualche parte, come questo:
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 : questo è solo in Scala 2.8 (grazie, Ken)