4

私はscalaを初めて使用し、次の問題に遭遇しました:

特定のタイプの要素のみを含む既存のコレクションのサブコレクションを取得したいと考えています。以下の作品:

class C(val name : String)
class D(name : String) extends C(name) { }

val collection = Set[C](new C("C1"),new D("D1"),new C("C2"),new D("D2"))
collection.collect{case d : D => d}.size must be === 2 // works

しかし、メソッド「onlyInstancesOf[Type]」でコレクション クラスを拡張しようとすると、これは機能しません。最初に私の実装:

object Collection {
    implicit def extendScalaCollection[E](coll : Traversable[E]) = new CollectionExtension[E](coll)
}

class CollectionExtension[E](coll : Traversable[E]) {

    def onlyInstancesOf[SpecialE <: E] : Traversable[SpecialE] = {
        coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]]
    }
}

したがって、この拡張機能を使用して実行すると:

collection.onlyInstancesOf[D].size must be === 2

.size が 2 ではなく 4 を返したというエラーが表示されます。

私がする時:

collection.onlyInstancesOf[D].foreach(e => println(e.name))

私は例外を受け取ります:

java.lang.ClassCastException: CollectionsSpec$$anonfun$1$C$1 cannot be cast to CollectionsSpec$$anonfun$1$D$1

したがって、明らかに、結果のセットには、フィルターで除外されるべき要素がまだ含まれています。

どうしてこうなったのかわからないのですが、どなたか説明していただけないでしょうか?

編集: Scala: Scala コード ランナー バージョン 2.8.0.final

4

4 に答える 4

10

コンパイラの警告に注意し、scalaコマンドラインオプションのチェックを外して追加します。

M:\>scala -unchecked
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21)
.
Type in expressions to have them evaluated.
Type :help for more information.

scala> class CollectionExtension[E](coll : Traversable[E]) {
     |
     |     def onlyInstancesOf[SpecialE <: E] : Traversable[SpecialE] = {
     |         coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]]
     |     }
     | }
<console>:8: warning: abstract type SpecialE in type pattern SpecialE is unchecked since it is eliminated by erasure
               coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]]
                                            ^
defined class CollectionExtension

警告は、コンパイラが実行できる最善のことは次と同等であることを意味します。

coll.collect({case special : AnyRef => special}).asInstanceOf[Traversable[_]]

型消去の詳細と、マニフェストを使用して回避する方法については、以下を参照してください。

https://stackoverflow.com/questions/tagged/type-erasure+scala

于 2010-09-07T16:04:31.373 に答える
6

他の人が指摘しているように、マニフェストはあなたを救うことができます。これは、非プリミティブに制限し、マニフェストをコレクションに保存せず、代わりにその場でリフレクションを使用して物事を把握する方法の例です。

class CollectionExtension[E <: AnyRef](coll : Traversable[E]) {
  def onlyInstancesOf[SpecialE <: E](implicit mf : Manifest[SpecialE]) : Traversable[SpecialE] = {
    coll.collect({
      case special if mf.erasure.isAssignableFrom(special.getClass) => special
    }).asInstanceOf[Traversable[SpecialE]]
  }
}

そしてここでそれは実行中です:

scala> val ce = new CollectionExtension(List(Some(1),Some(5),"This","Fox")) 
ce: CollectionExtension[java.lang.Object] = CollectionExtension@1b3d4787

scala> val opts = ce.onlyInstancesOf[Some[_]]
opts: Traversable[Some[_]] = List(Some(1), Some(5))

scala> val strings = ce.onlyInstancesOf[String] 
strings: Traversable[String] = List(This, Fox)
于 2010-09-07T16:22:47.043 に答える
4

Scala は JVM で実行されますが、残念ながら実行時に型パラメーターが消去されます: http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure。最初の例では、消去されていない位置に型を指定しているため、ランタイム コードは比較を行うことができます。2 番目の例では、SpecialE型が消去されているため、コードはすべてを返します。

scala のマニフェストを使用して、型消去によって失われた情報の一部を取り戻すことができます。

import scala.reflect.ClassManifest
class CollectionsExtension[E <: AnyRef](coll : Traversable[E]) {
  def onlyInstancesOf[SpecialE <: E](implicit m : Manifest[SpecialE]) : Traversable[SpecialE] = {
    coll.collect({case e if (ClassManifest.singleType(e) <:< m) => e}).asInstanceOf[Traversable[SpecialE]]
  }
}
于 2010-09-07T16:08:00.460 に答える
3

警告が言うように:

<console>:14: warning: abstract type SpecialE in type pattern SpecialE is unchecked since it is eliminated by erasure
               coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]]

の実装を見てみましょうcollect:

def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  val b = bf(repr)
  for (x <- this) if (pf.isDefinedAt(x)) b += pf(x)
  b.result
}

ここにはパターンマッチングがないことに注意してください。これが根本的な違いです。" " と書くとcollection.collect{case d : D => d}、コンパイラはあなたが話している型を正確に認識します: D.

一方、 を記述するcoll.collect({case special : SpecialE => special})と、コンパイラは type を認識しません。これは、が単なる型パラメーターSpecialEであるためです。SpecialEそのため、何が何であるかを認識するコードを生成することはできずSpecialE、実行時にはもう存在SpecialEしません。バイトコードは単に を使用しjava.lang.Objectます。

于 2010-09-07T16:09:28.537 に答える