Scala:根据类型参数过滤集合

drh*_*gen 4 collections scala type-erasure

基于这些对象的类型参数过滤对象集合的最佳方法是什么,假设我可以控制这两个类并且我需要协变过滤?

以下是一些无法正常运行的代码:

trait Foo
case class Foo1() extends Foo
trait ReadableFoo extends Foo {def field: Int}
case class Foo2(field: Int, flag: Boolean) extends ReadableFoo
case class Foo3(field: Int, name: String) extends ReadableFoo

case class Bar[+F <: Foo](foo: F)

val seq = Seq(
  Bar[Foo1](Foo1()),
  Bar[Foo2](Foo2(1,true)), 
  Bar[Foo3](Foo3(1,"Fooz"))
)

// Should keep one
val first = seq collect {case x: Bar[Foo2] => x}

// Should keep two
val both = seq collect {case x: Bar[ReadableFoo] => x}
Run Code Online (Sandbox Code Playgroud)

现在,我知道这是因为在编译之后case x: Bar[Foo1]通过类型擦除转换case x: Bar[_].我一直无法使用清单来解决这个问题.有没有办法添加一个成员类型(即memberType = F)Bar,我可以打开像case x if (x.memberType <:< ReadableFoo) => x

更新

0__很快找到了解决原始问题的好方法.稍微修改一下,case case字段本身就是一个集合:

case class Bar[+F <: Foo](foo: Seq[F])

val seq = Seq(
  Bar[Foo1](Seq(Foo1())),
  Bar[Foo2](Seq(Foo2(1,true))),
  Bar[ReadableFoo](Seq(Foo2(1,true), Foo3(1,"Fooz")))
)

// Should keep one
val first = seq collect {case x: Bar[Foo2] => x}

// Should keep two
val both = seq collect {case x: Bar[ReadableFoo] => x}
Run Code Online (Sandbox Code Playgroud)

我不确定这是可能的,因为它Seq可能是空的,因此没有要测试的元素.

0__*_*0__ 8

您可以将提取器与类型检查结合使用:

val first = seq collect { case x @ Bar(_: Foo2)        => x }
val both  = seq collect { case x @ Bar(_: ReadableFoo) => x }
Run Code Online (Sandbox Code Playgroud)

但是返回类型仍然是List[Bar[Foo]]......所以如果你需要它,使用这种方法你需要强制转换或重构Bar对象(case Bar(f: Foo2) => Bar(f)).


随着异构Seq我猜你正在寻找collectSeq本身?

case class Bar(seq: Seq[Foo])

def onlyFoo2(b: Bar) = Bar(b.seq.collect { case f: Foo2 => f })

onlyFoo2(Bar(Seq(Foo1(), Foo2(1, true))))
Run Code Online (Sandbox Code Playgroud)