在 scala 中匹配多个案例类

我正在对一些案例类进行匹配,希望以同样的方式处理其中两个案例。就像这样:

abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo




def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(sb) | C(sc) => "B"
case _ => "default"
}
}

但是当我这样做的时候,我得到了一个错误:

(fragment of test.scala):10: error: illegal variable in pattern alternative
case B(sb) | C(sc) => "B"

我可以得到它的工作,我从 B 和 C 的定义中删除了参数,但是我怎样才能与参数匹配呢?

97953 次浏览

Well, it doesn't really make sense, does it? B and C are mutually exclusive, so either sb or sc get bound, but you don't know which, so you'd need further selection logic to decide which to use (given that they were bound to a Option[String], not a String). So there's nothing gained over this:

  l match {
case A() => "A"
case B(sb) => "B(" + sb + ")"
case C(sc) => "C(" + sc + ")"
case _ => "default"
}

Or this:

  l match {
case A() => "A"
case _: B => "B"
case _: C => "C"
case _ => "default"
}

Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:

def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(_) | C(_) => "B"
case _ => "default"
}
}

If you must, must, must extract the parameter and treat them in the same code block, you could:

def matcher(l: Foo): String = {
l match {
case A() => "A"
case bOrC @ (B(_) | C(_)) => {
val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
"B(" + s + ")"
}
case _ => "default"
}
}

Though I feel it would be much cleaner to factor that out into a method:

def doB(s: String) = { "B(" + s + ")" }


def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(s) => doB(s)
case C(s) => doB(s)
case _ => "default"
}
}

There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.

 object MuliCase {
abstract class Foo
case object A extends Foo


trait SupportsS {val s: String}


type Stype = Foo {val s: String}


case class B(s:String) extends Foo
case class C(s:String) extends Foo


case class D(s:String) extends Foo with SupportsS
case class E(s:String) extends Foo with SupportsS


def matcher1(l: Foo): String = {
l match {
case A        => "A"
case s: Stype => println(s.s); "B"
case _        => "default"
}
}


def matcher2(l: Foo): String = {
l match {
case A            => "A"
case s: SupportsS => println(s.s); "B"
case _            => "default"
}
}


def main(args: Array[String]) {
val a = A
val b = B("B's s value")
val c = C("C's s value")


println(matcher1(a))
println(matcher1(b))
println(matcher1(c))


val d = D("D's s value")
val e = E("E's s value")


println(matcher2(d))
println(matcher2(e))
}
}

The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.