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

时间:2011-05-09 07:05:46

标签: scala filter type-inference 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
  }
}

4 个答案:

答案 0 :(得分:7)

你是对的,不知何故,应该有一种机制可以让你避免施法。这样的演员阵容将是丑陋和多余的,因为它无论如何已经出现在过滤器中。然而,find并不关心它所获得的谓词的形状;如果Option[A]是集合元素的静态类型,它只应用它并返回A

您需要的是collect功能:

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

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

view

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

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

修改:最后,如果您使用的是Scala 2.9,则绝对应该按照Kevin Wright's answer中的说明使用val bossOpt = People.all.view.collect { case boss: Authority => boss }.headOption bossOpt.foreach(_.giveOrder) // happens only if a boss was found

答案 1 :(得分:5)

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

如果使用Scala 2.9,您还可以使用collectFirst方法,这样您就可以避免所有繁琐的viewheadheadOption

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
}

答案 2 :(得分:0)

试试这个

boss.asInstanceOf[Authority].giveOrder

或者

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

答案 3 :(得分:0)

你真的想找到第一个吗? find就是这样做的。如果您想查找所有Authority s:

,请考虑使用Jean-Philippe的解决方案
val authorities = People.all.collect {
  case boss: Authority => boss
}.foreach(_.giveOrder)