Pergunta

Estou com problemas que se estende de uma classe de base que se estende Pedi [base]. Minha classe derivada não pode estender ordenada [Derivado] por isso não pode ser usado como uma chave em um TreeMap. Se eu criar um TreeMap [base] e depois é só override comparar em que trabalhos derivados, mas não é o que eu quero. Eu gostaria de ser capaz de ter a classe derivada como uma chave. Existe uma maneira de contornar isso?

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
}

Editar

Este discussão na lista de discussão scala parece ser muito relevante, mas ele me perde um pouco.

Foi útil?

Solução

Você pode usar uma conversão de tipo de B para Ordenada [B]:

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

Eu acho que B tem sempre que ser um subtipo de A que implica Order [A] whoes tipo A é invariável. Não é possível definir um método de comparar segundo a implementar Pedido [B] com o mesmo tipo errasure como o método de comparar a partir de Pedidos [A].

Como alternativa, você pode definir um versões tipo implícito de B para Ordenado [B]:

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

Esta deve ser válido. Eu não estou ciente de uma maneira de expressar isso no sistema do tipo sem moldes.

Outras dicas

O Ordered característica leva um parâmetro. Um parâmetro de tipo, concedido, mas funciona como qualquer outro parâmetro. Quando você estendê-lo duas vezes, na classe base e na subclasse, você não é "importar" duas versões de Ordered. Em vez disso, linearização das classes ocorre, e importá-lo apenas uma vez. Por essa razão, você não pode passar dois parâmetros diferentes para ele.

Agora, há uma razão pela qual TreeMap não requer uma subclass de Ordered, apenas uma conversão de sua classe a um Ordered dele. É precisamente para fazer tais coisas possíveis. Em vez de estender essas coisas diretamente, você deve implícitos para eles:

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

Você poderia colocar uma ordem implícita [B] em algum lugar escopo, como este:

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

Editar : Este é apenas em Scala 2,8 (graças, Ken)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top