web-dev-qa-db-ja.com

Scala-関連タイプのタプルのパターンマッチング

次のクラス階層があります。

class A
class B extends A
class C extends A

次に、これらのクラスのインスタンスを取得する別のクラスがあり、次のようなパターンマッチングの2つのケースが可能なメソッドがあります。

class D (one: A, two: A) {

  def work {
    (one, two) match {
      case (o, t): (B, B) => ... blablabla
      case (o, t): (B, C) => ... blablabla
      case _ =>
    }
  }
}

ただし、2番目のケース(B, C)を優先して一致を解決する必要がある場合は、(B, B)として解決を試み、C cannot be cast to Bというクラスキャスト例外が発生します。どうして?何をすべきか?どうすればこれを回避できますか?

21
noncom

構文が正しくありません(コンパイルできません)。

これはうまくいきます:

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
}
29
Brian Smith

問題は、いつものように、消去されたタイプです。 (B,C)Tuple2[B,C]の構文糖であり、実行時にTuple2に消去されます。 caseステートメントは、(B,C)Tuple2と一致することを確認しますが、キャストに失敗します。

あなたの場合、最も簡単な解決策は、タプルでラップするのではなく、「1」と「2」を個別に照合することです:

one match {
  case o : B => two match {
    case p : C => ...
    case p : B => ...
  }
  ... 
}

それほどきれいではありませんが、同じ問題が発生することはありません。

編集:実際には、私はブライアン・スミスのソリューションを使います-外部ではなくタプルの内部で一致します。同様の方法で問題を回避しますが、見栄えが良くなります。

8
Submonoid

私はこのコードを機能させました。
最初に、クラス定義にケースを追加しました。

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メソッドを追加します。

2
tgr