Подтвердить что ты не робот

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. Зачем? Что делать? Как я могу обойти это?

4b9b3361

Ответ 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.