У меня есть class Foo extends Bar и List или другая коллекция базового класса:

val bars: Iterable[Bar] 

Мне нужно извлечь все Foo элементы из коллекции. Ниже приведен код:

val fooes: Iterable[Foo] = bars
    .filter(x => Try(x.isInstanceOf[Foo]).isSuccess))
    .map(_.isInstanceOf[Foo])

Есть ли краткий подход?

1
Loom 7 Май 2020 в 13:53

2 ответа

Пара возможных переписок стоит запомнить в общем

Поэтому следующее не рекомендуется

bars
  .filter { _.isInstanceOf[Foo] }
  .map    { _.asInstanceOf[Foo] }

Можно переписать в идиоматический стиль

bars collect { case foo: Foo => foo }

... написание типовых тестов и приведений довольно многословно в Scala. Это намеренно, потому что это не поощряемая практика. Вам обычно лучше использовать сопоставление с набранным рисунком. Это особенно верно, если вам нужно выполнить как проверку типа, так и приведение типа, потому что обе операции затем объединяются в одно сопоставление с образцом.

Обратите внимание, что природа типизированного шаблона все еще является проверкой типа во время выполнения с последующим приведением типа во время выполнения, то есть она просто представляет более приятный стиль одежда не увеличивает безопасность типов. Например

scala -print -e 'lazy val result: String = (42: Any) match { case v: String => v }'

Расширяется до чего-то вроде

<synthetic> val x1: Object = scala.Int.box(42);
if (x1.$isInstanceOf[String]()) {
  <synthetic> val x2: String = (x1.$asInstanceOf[String]());
  ...
}

Где мы ясно видим проверку типа isInstanceOf, за которой следует приведение типа asInstanceOf.

2
Mario Galic 7 Май 2020 в 23:36
val fooes: Iterable[Foo] = bars.collect{case foo:Foo => foo}

Метод .collect() принимает частичную функцию в качестве параметра. В этом случае функция определяется только для типов Foo. Все остальные игнорируются.

7
jwvh 7 Май 2020 в 11:11