具有共同特征的模式匹配案例 类

Pattern Matching Case Classes with Common Trait

我正在编写一些使用模式匹配的代码。在测试中我发现了一个奇怪的结果:

object Example extends App {

  trait Human {
    def sing(): Unit
  }

  case class Son(name: String) extends Human {
    override def sing(): Unit = println("son " + name)
  }

  case class Daughter(name: String) extends Human {
    override def sing(): Unit = println("daughter " + name)
  }
  
  val jack = Son("jack")
  val sonia = Daughter("sonia")

  def f1(lst: List[Human]) = {
    lst match {
      case a: List[Son] => println("human is son")
      case b: List[Daughter] => println("human is daughter")
    }
  }

  f1(List(jack))
  f1(List(sonia))
}

这两个都打印“human is a son”。有没有解决的办法?我可以看到编译器将 Son 和 Daughter 与 Human 匹配。但是有没有办法让它区分这两者呢?

重构代码以摆脱 List[Human] 修复了这个问题。

我们观察到,在这种特定情况下,我们可以简单地提取列表的第一个元素,而不是对列表进行操作。

看来您确实需要重构您的设计。你不应该需要在运行时检查列表中的类型元素——如果你想要动态调度,你可以有一个重写的方法,或者你可以为 List[Son]s 和 List[Daughter].[= 使用单独的方法19=]

如果您真的想要确保列表中的所有元素都是sons/daughters,您可以使用forall

def f1(lst: List[Human]) =
    if (lst.forall(_.isInstanceOf[Son])) println("human is son")
    else if (lst.forall(_.isInstanceOf[Daughter])) println("human is daughter")

不过这不是很好。如果列表中有儿子 女儿,或者可能完全是第三种类型怎么办?

我推荐 2 种不同的方法 - 一种用于 Sons,一种用于 Daughters。我还会将您的 Human 特征密封,这样就无需处理新的实现。

def f1(lst: List[Daughter]) = println("human is daughter")
//DummyImplicit is a workaround for type erasure, otherwise, they'd have the same signature
def f1(lst: List[Son])(implicit d: DummyImplicit) = println("human is son")

您也可以使用类型类,尽管在这里似乎不值得

def f1[A <: Human](lst: List[A])(implicit idr: Identifier[A]) =
  idr.identify(lst)

sealed trait Identifier[A <: Human] {
  def identify(lst: List[A]): String
}
object Identifier {
  implicit val sonIdentifier: Identifier[Son] = _ => "human is son"
  implicit val daughterIdentifier: Identifier[Daughter] = _ => "human is daughter"
}