4

両方のクラスを制御でき、共変フィルタリングが必要であると仮定して、それらのオブジェクトの型パラメーターに基づいてオブジェクトのコレクションをフィルター処理する最良の方法は何ですか?

正しく機能しないコードは次のとおりです。

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 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が空である可能性があり、したがってテストする要素がないため、これが可能かどうかはわかりません。

4

2 に答える 2

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))))
于 2012-07-03T23:36:33.450 に答える
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のサブタイプのマニフェストを返すことを約束できます(マニフェストを使用しようとしたときにこれが問題だったと思いますか?

val first = seq collect {case x if x.manifest <:< manifest[Foo2] => x}
val both = seq collect {case x if x.manifest <:< manifest[ReadableFoo] => x}

それでも、マニフェストを使用すると、常に少しハッキーな感じがします。別のアプローチを使用して、タイプのマッチングと具体化にできるだけ依存しないようにすることができるかどうかを確認します。

于 2012-07-04T03:04:16.493 に答える