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 '.

È stato utile?

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 sottoclasse di Ordinato , solo una conversione dalla tua classe in un Ordinato . È proprio per rendere possibili queste cose. Invece di estendere queste cose direttamente, dovresti implicirle:

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)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top