Как получить правильный тип возвращаемого значения при использовании фильтра на основе типа в Scala - PullRequest
3 голосов
/ 09 мая 2011

Следующее не компилируется. Нужно ли сначала разыграть человека?

 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 ]

7 голосов
/ 09 мая 2011

Вы правы, думая, что каким-то образом должен быть механизм, который позволит вам избежать каста. Такое приведение будет уродливым и избыточным, так как оно все равно появляется в фильтре. find, однако, совершенно не заботится о форме предиката, который он получает; он просто применяет его и возвращает Option[A], если 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, как объяснено в Ответ Кевина Райта .

5 голосов
/ 09 мая 2011

Жан-Филипп хорошо ответил, но можно пойти еще дальше ...

При использовании 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

boss по-прежнему Option[Person], я рекомендую вам сохранить его таким образом, ради более безопасного кода. Если вы хотите, вы также можете использовать для понимания, что некоторые люди еще чище:

for(boss <- People.all.collectFirst { case x: Authority => x }) {
  boss.giveOrder // happens only if a boss was found
}
0 голосов
/ 09 мая 2011

Вы действительно хотите найти только первый?find делает именно это.Попробуйте найти решение от Jean-Philippe, если хотите найти все Authority s:

val authorities = People.all.collect {
  case boss: Authority => boss
}.foreach(_.giveOrder)
0 голосов
/ 09 мая 2011

Попробуйте это

boss.asInstanceOf[Authority].giveOrder

или это

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