【问题标题】:Pattern Matching Case Classes with Common Trait具有共同特征的模式匹配案例类
【发布时间】:2020-11-19 03:42:10
【问题描述】:

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

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))
}

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

【问题讨论】:

  • 由于类型擦除你不能检查lstList[Son]还是List[Daughter]你只能检查它是否是一个列表(你已经知道了,所以它没用)。你真正想做的是什么?可能有解决方法。
  • 感谢@LuisMiguelMejíaSuárez,我基本上是在重构一些现有代码以消除冗余。其中一个功能基本上采用 List[Human]。我想以某种方式将模式匹配到两个代码路径中;一份给儿子,一份给女儿。但我会看看我是否可以尝试另一种方式 - 似乎擦除不允许我这样做。
  • 有一篇关于它的好帖子。 gist.github.com/jkpl/…
  • @finite_diffidence 如果您想编辑问题(或打开一个新问题),并提供有关潜在问题的更多详细信息,我们或许可以提出替代方案。
  • 谢谢我会打开一个新的,虽然我想我设法通过删除一个列表按照你的建议重构了代码。感谢您的反馈,有时只需与某人交谈即可解决问题!

标签: scala pattern-matching traits case-class


【解决方案1】:

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

如果你真的想确保列表中的所有元素都是儿子/女儿,你可以使用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")

不过,这不是很好。如果有一个包含儿子女儿的列表,或者可能有第三种类型的列表怎么办?

我推荐两种不同的方法 - 一种用于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"
}

【讨论】:

    【解决方案2】:

    重构代码以摆脱 List[Human] 解决了该问题。

    据观察,我们可以在此特定场景中简单地提取列表的第一个元素,而不是对列表进行操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-27
      • 2020-11-28
      • 1970-01-01
      • 2017-04-25
      • 2013-11-28
      • 2016-11-26
      • 2012-10-22
      • 1970-01-01
      相关资源
      最近更新 更多