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

发布于 2024-11-05 13:07:22 字数 505 浏览 2 评论 0原文

以下内容无法编译。我需要先选人物吗?

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

The following doesn't compile. Do I need to cast the person first?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

女皇必胜 2024-11-12 13:07:22

您的想法是对的,应该有一种机制可以让您避免强制转换。这样的强制转换将是丑陋且多余的,因为它已经出现在过滤器中了。然而,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 的回答

You're right thinking that somehow, there should be a mechanism that lets you avoid casting. Such a cast would be ugly and redundant, as it already appears in the filter anyway. find, however, does not care at all about the shape of the predicate it gets; it just applies it and returns an Option[A] if A is the static type of the collection's elements.

What you need is the collect function:

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

collect creates a new collection. If you want to avoid this (if you're really only interested in the first element which is of kind Authority) in case of a potentially very long list of potential bosses, you may want to switch to a view to have it evaluated lazily:

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

Finally, unless you're absolutely sure that there is always at least one boss in your list, you should really test whether or not the search was successful, e.g. like this:

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

Edit: Finally, if you're using Scala 2.9, you should definitely use collectFirst as explained in Kevin Wright's answer.

岁吢 2024-11-12 13:07:22

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-compression,有些人更清楚:

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

Jean-Philippe's answer is good, but it's possible to go one step further...

If using Scala 2.9, you'll also have a collectFirst method available, allowing you to avoid all those tedious view's, head's and headOption's

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

boss is still an Option[Person], I recommend that you keep it this way for the sake of safer code. If you want, you can also use a for-comprehension, which some people to be cleaner still:

for(boss <- People.all.collectFirst { case x: Authority => x }) {
  boss.giveOrder // happens only if a boss was found
}
墨小沫ゞ 2024-11-12 13:07:22

试试这个

boss.asInstanceOf[Authority].giveOrder

或这个

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

Try this

boss.asInstanceOf[Authority].giveOrder

or this

val boss =  People.all.find { _.isInstanceOf [Authority] }.get.asInstanceOf[Person with Authority]
寻找一个思念的角度 2024-11-12 13:07:22

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

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

Do you really want to find just the first one? find does exactly this. Consider using the solution from Jean-Philippe if you want to find all Authoritys:

val authorities = People.all.collect {
  case boss: Authority => boss
}.foreach(_.giveOrder)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文