Scala集合按类型过滤

Rag*_*nir 4 types scala filter type-erasure scala-collections

我是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
Run Code Online (Sandbox Code Playgroud)

但是当我尝试使用方法"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]]
    }
}
Run Code Online (Sandbox Code Playgroud)

所以当我使用这个扩展并执行时:

collection.onlyInstancesOf[D].size must be === 2
Run Code Online (Sandbox Code Playgroud)

我得到一个错误,.size返回4而不是2.此外,我检查,结果实际上包含C1和C2虽然它不应该.

当我做:

collection.onlyInstancesOf[D].foreach(e => println(e.name))
Run Code Online (Sandbox Code Playgroud)

我得到了例外:

java.lang.ClassCastException: CollectionsSpec$$anonfun$1$C$1 cannot be cast to CollectionsSpec$$anonfun$1$D$1
Run Code Online (Sandbox Code Playgroud)

显然,结果集仍然包含应该过滤掉的元素.

我不知道为什么会发生这种情况,任何人都能解释一下吗?

编辑:Scala:Scala代码运行版版本2.8.0.final

ret*_*nym 10

注意编译器警告,并添加-unchecked 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
Run Code Online (Sandbox Code Playgroud)

警告意味着编译器可以做的最好的事情相当于:

coll.collect({case special : AnyRef => special}).asInstanceOf[Traversable[_]]
Run Code Online (Sandbox Code Playgroud)

有关类型擦除的更详细说明以及使用清单解决方法,请参阅:

/sf/ask/tagged/type-erasure/+scala


Rex*_*err 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]]
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里它是在行动:

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)
Run Code Online (Sandbox Code Playgroud)