Scala - сопоставление шаблонов с кортежем связанных типов
У меня есть следующая иерархия классов:
class A
class B extends A
class C extends A
тогда существует еще один класс, который принимает экземпляры этих классов, и существует метод, в котором возможны два случая сопоставления шаблонов:
class D (one: A, two: A) {
def work {
(one, two) match {
case (o, t): (B, B) => ... blablabla
case (o, t): (B, C) => ... blablabla
case _ =>
}
}
}
Однако, когда он должен решить совпадение в пользу второго случая (B, C)
, он пытается разрешить его как (B, B)
и придумывает исключение класса cast, которое C cannot be cast to B
. Зачем? Что делать? Как я могу обойти это?
Ответы
Ответ 1
Ваш синтаксис не совсем прав (не компилируется).
Это работает, хотя:
object Matcher extends App {
class A
class B extends A
class C extends A
class D(one: A, two: A) {
def work {
(one, two) match {
case (o: B, t: B) => println("B")
case (o: B, t: C) => println("C")
case _ =>
}
}
}
val d1 = new D(new B, new B)
val d2 = new D(new B, new C)
d1.work
//B
d2.work
//C
}
Ответ 2
Проблема, как всегда, стирается. (B,C)
является синтаксическим сахаром для Tuple2[B,C]
, который стирается до Tuple2
во время выполнения. Оператор case проверяет, что (B,C)
соответствует Tuple2
, но затем не может выполнить его.
В вашем случае самым простым решением было бы совпадение "один" и "два" в отдельности, а не обертывание их в кортеж:
one match {
case o : B => two match {
case p : C => ...
case p : B => ...
}
...
}
Это не так красиво, но он не будет страдать от тех же проблем.
Изменить: На самом деле, я бы пошел с решением Брайана Смита - совпадающим внутри кортежа, а не снаружи. Это позволяет избежать проблемы аналогичным образом, но выглядит лучше.
Ответ 3
Я создал этот код.
Во-первых, я добавил случай к определению вашего класса.
case class A
case class B extends A
case class C extends A
Во-вторых, я изменил work
.
class D(one: A, two: A) {
def work {
(one, two) match {
case (o: B, t: B) => println("BB")
case (o: B, t: C) => println("BC")
case (o: C, t: C) => println("CC")
case _ => println("AA")
}
}
}
Теперь, что я получил:
new D(B(),B()).work => BB
new D(B(),C()).work => BC
new D(C(),C()).work => CC
new D(A(),B()).work => AA
case
добавляет метод apply и unapply.