【问题标题】:How to get the proper return type when using a filter based on type in Scala在Scala中使用基于类型的过滤器时如何获得正确的返回类型
【发布时间】:2011-05-09 07:05:46
【问题描述】:

以下内容无法编译。我需要先投射该人吗?

 object People {
  def all = List(
    new Person("Jack", 33),
    new Person("John", 31) with Authority,
    new Person("Jill", 21),
    new Person("Mark", 43)
  )
}

class Person(val name: String, val age: Int) 

trait Authority {
  def giveOrder {
    println("do your work!")
  }
}

object Runner {
  def main(args:List[String]) {
    val boss = People.all.find { _.isInstanceOf [Authority] }.get
    boss.giveOrder // This line doesnt compile
  }
}

【问题讨论】:

  • 请添加编译器错误。 Boss 不见了。

标签: scala filter type-inference scala-collections


【解决方案1】:

您的想法是正确的,应该有一种机制可以让您避免强制转换。这样的演员阵容将是丑陋和多余的,因为它已经出现在过滤器中。但是,find 根本不关心它得到的谓词的形状;如果A 是集合元素的静态类型,它只是应用它并返回Option[A]

你需要的是collect函数:

val boss = People.all.collect { case boss: Authority => boss }.head

collect 创建一个新集合。如果你想避免这种情况(如果你真的只对第一个类似 Authority 的元素感兴趣),以防潜在老板的列表可能很长,你可能想要切换到 view 以拥有它懒惰地评估:

val boss = People.all.view.collect { case boss: Authority => boss }.head

最后,除非您绝对确定您的列表中总是至少有一个老板,否则您应该真正测试搜索是否成功,例如像这样:

val bossOpt = People.all.view.collect { case boss: Authority => boss }.headOption
bossOpt.foreach(_.giveOrder) // happens only if a boss was found

编辑:最后,如果您使用的是 Scala 2.9,则绝对应该使用 collectFirst,如 Kevin Wright's answer 中所述。

【讨论】:

  • 另外,如果你想把boss实例也当作一个Person,你可以使用这个collect语句来代替:case boss:Person with Authority => boss
  • 不知道在Option 上使用foreach。谢谢!
【解决方案2】:

Jean-Philippe 的回答很好,但可以更进一步……

如果使用 Scala 2.9,您还可以使用 collectFirst 方法,让您避免所有那些乏味的 view's、head's 和 headOption's

val boss = People.all.collectFirst { case x: Authority => x }
boss.foreach(_.giveOrder) // happens only if a boss was found

boss 仍然是Option[Person],为了代码更安全,我建议您保持这种方式。如果你愿意,你也可以使用 for-comprehension,有些人还是比较干净的:

for(boss <- People.all.collectFirst { case x: Authority => x }) {
  boss.giveOrder // happens only if a boss was found
}

【讨论】:

  • +1 酷,不知道collectFirst。它最适合这里。
  • 不足为奇,它还没有稳定发布该语言(虽然很接近,在撰写本文时我们已经在 RC4 上)
【解决方案3】:

试试这个

boss.asInstanceOf[Authority].giveOrder

或者这个

val boss =  People.all.find { _.isInstanceOf [Authority] }.get.asInstanceOf[Person with Authority]

【讨论】:

  • 我并没有对自己投反对票,但我想这是由于使用了isInstanceOf(通常是代码味道)而不是与collect的模式匹配
  • isInstanceOf, asInstanceOf, Option.get 都不是很漂亮……
【解决方案4】:

您真的只想找到第一个吗? find 正是这样做的。如果您想查找所有Authoritys,请考虑使用 Jean-Philippe 的解决方案:

val authorities = People.all.collect {
  case boss: Authority => boss
}.foreach(_.giveOrder)

【讨论】:

    猜你喜欢
    • 2011-04-04
    • 2020-05-02
    • 2015-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多