在Scala中,如何在运行时按照具体类型进行过滤?

时间:2014-05-03 00:22:22

标签: scala type-erasure

我有一个Scala集合,其中包含不同子类型的对象。

abstract class Base

class A extends Base

class B extends Base

val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)

我想过滤掉所有A个对象或B个对象。如果我知道我想在编译时过滤的对象,我可以很容易地做到这一点。

s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs

如果我只知道要在运行时过滤的对象类型,我可以这样做吗?我想写一个这样的函数。

def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])

t指示我是否需要AB类型的对象。

当然,由于类型擦除,我实际上无法以这种方式编写它。有没有惯用的Scala方法来解决这个使用类型标签?我一直在阅读Scala类型标记文档和相关的StackOverflow帖子,但我无法弄明白。

2 个答案:

答案 0 :(得分:6)

这已经出现了几次。重复,有人吗?

scala> trait Base
defined trait Base

scala> case class A(i: Int) extends Base 
defined class A

scala> case class B(i: Int) extends Base 
defined class B

scala> val vs = List(A(1), B(2), A(3))
vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))

scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]

scala> f[A](vs)
res0: List[A] = List(A(1), A(3))

答案 1 :(得分:1)

类型擦除会破坏类型参数中的任何信息,但对象仍然知道它们属于哪个类。因此,我们不能过滤任意类型,但我们可以按类或接口/特征进行过滤。 ClassTag优于TypeTag

import scala.reflect.ClassTag

def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
  case x: T => x
}

我们可以使用:

scala> filterType[B](s)
res29: List[B] = List(B@42096939)

scala> filterType[Base](s)
res30: List[Base] = List(A@8dbc09c, A@625f8cc7, B@42096939)

如果类型T不是通用的,则此方法在运行时是安全的。如果有class C[T] extends Base我们无法安全地过滤C[String]