如何在Scala中使用基于类型的过滤器时获取正确的返回类型

npi*_*piv 3 scala type-inference filter scala-collections

以下内容无法编译.我需要先安排这个人吗?

 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
  }
}
Run Code Online (Sandbox Code Playgroud)

Jea*_*let 7

你是在思考,不管怎么说,应该有一种让你避免施法的机制.这样的演员阵容将是丑陋和多余的,因为它无论如何已经出现在过滤器中.find然而,它根本不关心它得到的谓词的形状; 它只是应用它并返回一个Option[A]if A是集合元素的静态类型.

你需要的是collect功能:

val boss = People.all.collect { case boss: Authority => boss }.head
Run Code Online (Sandbox Code Playgroud)

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

val boss = People.all.view.collect { case boss: Authority => boss }.head
Run Code Online (Sandbox Code Playgroud)

最后,除非你绝对确定列表中总是至少有一个boss,否则你应该真的测试搜索是否成功,例如:

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

编辑:最后,如果你正在使用Scala 2.9,你肯定应该collectFirst按照Kevin Wright的回答中的说明使用.

  • 此外,如果您想将boss实例视为Person,则可以使用此collect语句:`case boss:具有权限的人=> boss`. (4认同)

Kev*_*ght 5

让 - 菲利普的回答很好,但有可能更进一步......

如果使用Scala的2.9,你也有一个collectFirst可行的方法,可以让你避免那些繁琐view的,head年代和headOption

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

boss仍然是一个Option[Person],我建议你为了更安全的代码保持这种方式.如果你愿意,你也可以使用for-comprehension,有些人仍然更清洁:

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