Scala 集合按类型过滤
我是 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 返回 4 而不是 2。另外,我检查过,结果实际上包含 C1 和 C2,尽管它不应该包含。
当我这样做时:
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
im new to scala and ran into the following problem:
I want to get a subcollection of an existing collection that only contains elements of a specific type. The following works:
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
But when i try to extend the collection classes with a method "onlyInstancesOf[Type]" this does not work. First my implementation:
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]]
}
}
So when i use this extension and execute:
collection.onlyInstancesOf[D].size must be === 2
I get an error that .size returned 4 and not 2. Also, i checked, the result actually contains C1 and C2 though it should not.
When i do:
collection.onlyInstancesOf[D].foreach(e => println(e.name))
I get the exception:
java.lang.ClassCastException: CollectionsSpec$anonfun$1$C$1 cannot be cast to CollectionsSpec$anonfun$1$D$1
So obviously the resulting set still contains the elements that should have been filtered out.
I dont get why this happens, can anyone explain this?
Edit:
Scala: Scala code runner version 2.8.0.final
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
注意编译器警告,并添加 -unchecked 你的 scala 命令行选项。
该警告意味着编译器可以做的最好的事情相当于:
有关类型擦除的更详细说明以及使用清单解决该问题的方法,请参阅:
https://stackoverflow.com/questions/tagged/type-erasure+scala
Pay attention to the compiler warnings, and add -unchecked your scala command line options.
The warning means that the best the compiler can do is equivalent to:
For a more detailed explanation of type erasure, and ways you can work around it with Manifests, see:
https://stackoverflow.com/questions/tagged/type-erasure+scala
正如其他人指出的那样,清单可以拯救您。下面是一个示例,说明如何将自己限制为非基元,并假设我们不想在集合中存储清单,而是当场使用反射来解决问题:
这里是实际操作:
As others have pointed out, manifests can rescue you. Here's an example of how, restricting ourselves to non-primitives, and assuming we don't want to store manifests in our collections but instead use reflection on the spot to figure things out:
and here it is in action:
Scala 在 JVM 上运行,不幸的是,它会在运行时擦除类型参数:http://en.wikipedia。 org/wiki/Generics_in_Java#Type_erasure。在第一个示例中,您将类型置于非擦除位置,因此运行时代码可以进行比较。在第二个示例中,
SpecialE
类型被删除,因此代码将返回所有内容。你可以使用 scala 的 Manifests 来重新获得一些因类型擦除而丢失的信息:
Scala runs on the JVM, which unfortunately erases type parameters at runtime: http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure. In your first example, you give the type in a non-erased position and so the runtime code can do the comparison. In the second example, the
SpecialE
type is erased, and hence the code will return everything.You can use scala's Manifests to regain some of the information lost by type erasure:
正如警告所说:
让我们看看
collect
的实现:请注意,这里没有模式匹配。这是根本区别 - 当您编写“
collection.collect{case d : D => d}
”时,编译器确切地知道您正在谈论的类型:D
。另一方面,当您编写
coll.collect({casespecial : SpecialE =>special})
时,编译器不知道SpecialE
是什么类型,因为 < code>SpecialE 只是一个类型参数。因此它无法生成知道SpecialE
是什么的代码,并且在运行时,不再有SpecialE
—— 字节码只使用java.lang .对象
。As the warning say:
Let's see the implementation of
collect
:Note that there's no pattern matching in here. This is the fundamental difference -- when you write "
collection.collect{case d : D => d}
" the compiler knows exactly what type you are talking about:D
.On the other hand, when you write
coll.collect({case special : SpecialE => special})
, the compiler doesn't know what typeSpecialE
, becauseSpecialE
is just a type parameter. So it can't generate code that knows whatSpecialE
is, and, at run-time, there's noSpecialE
anymore -- the bytecode just usesjava.lang.Object
.