Estender scala classe que estende ordenou
-
10-07-2019 - |
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.
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)