根据这些对象的类型参数过滤对象集合的最佳方法是什么,假设我可以控制这两个类并且我需要协变过滤?
以下是一些无效的代码:
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}
现在,我知道这是因为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}
我不确定这是否可行,因为Seq
可能为空,因此没有要测试的元素。
答案 0 :(得分:8)
您可以将提取器与类型检查结合使用:
val first = seq collect { case x @ Bar(_: Foo2) => x }
val both = seq collect { case x @ Bar(_: ReadableFoo) => x }
但是返回类型仍然是List[Bar[Foo]]
...所以如果你需要,那么使用这种方法你需要强制转换或重新构造Bar
对象(case Bar(f: Foo2) => Bar(f)
)
使用异构Seq
我猜您在collect
本身上寻找Seq
?
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))))
答案 1 :(得分:1)
我不知道提取器技巧的类型检查,所以我对你的拳头问题的初步解决方案会有所不同。我会为ReadableFoo
object ReadableFoo { def unapply(x: ReadableFoo) = Some(x.field) }
然后你可以做
val first = seq collect { case x @ Bar(Foo2(_,_)) => x }
val both = seq collect { case x @ Bar(ReadableFoo(_)) => x }
但是对于您更新的代码,我认为您需要拖动清单。
case class Bar[+F <: Foo : Manifest](foo: Seq[F]) {
def manifest = implicitly[Manifest[_ <: F]]
}
由于Bar是协变的而且Manifest是不变的,我们不能简单地承诺返回Manifest [F]但是F的某个子类型的Manifest(我猜这是你在尝试使用清单时的问题?)
之后你可以做到
val first = seq collect {case x if x.manifest <:< manifest[Foo2] => x}
val both = seq collect {case x if x.manifest <:< manifest[ReadableFoo] => x}
尽管如此,使用清单总是让人觉得有点hacky。我会看看我是否可以使用不同的方法,并尽可能少地依赖类型匹配和具体化。