Frage

Ich habe Probleme, eine Basisklasse erstreckt, die [Basis] bestellt erstreckt. Meine abgeleitete Klasse kann nicht so bestellt [Abgeleitete] verlaufen kann nicht als Schlüssel in einem TreeMap verwendet werden. Wenn ich eine TreeMap [Basis] und dann außer Kraft setzt nur vergleichen in Abgeleitet erstellen, das funktioniert, aber es ist nicht das, was ich will. Ich mag die abgeleitete Klasse als Schlüssel haben können. Gibt es eine Möglichkeit, um dieses?

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
}

Bearbeiten

Diese Diskussion auf der scala Mailingliste erscheint sehr relevant, aber es verliert mich ein wenig.

War es hilfreich?

Lösung

Sie können eine Typumwandlung von B verwenden, um bestellt [B]:

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

Ich denke, B hat immer ein Subtyp von A zu sein, die Bestellung impliziert [A] whoes Typ A invariant ist. Es kann nicht ein zweite vergleichen Verfahren zu implementieren Bestellung [B] mit dem gleichen Typ errasure als die Vergleichsmethode aus Bestellen [A].

definieren

Alternativ können Sie eine implizite Typ Versionen von B bis bestellt [B] definieren:

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

Dies sollte gültig sein. Ich bin mir nicht bewusst eine Art und Weise, ohne Abgüsse dies in der Art System zum Ausdruck bringen.

Andere Tipps

Das Merkmal Ordered nimmt einen Parameter. Ein Typ-Parameter, gewährt, aber es funktioniert wie jeder andere Parameter. Wenn Sie sie zweimal verlängern, in der Basisklasse und in der Unterklasse, sind Sie nicht zwei Versionen von Ordered „Import“. Stattdessen Linearisierung der Klassen erfolgt, und Sie importieren nur einmal. Aus diesem Grunde können Sie nicht zwei verschiedene Parameter übergeben.

Jetzt gibt es einen Grund, warum TreeMap kein subclass von Ordered erfordert, nur eine Umwandlung von Ihrer Klasse zu einem Ordered davon. Es ist genau solche Dinge möglich zu machen. Statt diese Dinge direkt zu verlängern, sollten Sie für sie implicits:

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

Sie können eine implizite Reihenfolge setzen [B] in Umfang irgendwo, wie folgt aus:

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

Bearbeiten : Dies ist nur in Scala 2.8 (Danke, Ken)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top