您需要提供ClassTag,而不是TypeTag,并使用模式匹配。 ClassTags 与模式匹配配合得很好。您甚至可以使用collect 方法同时执行filter 和map:
def filter[T, T2](data: Traversable[T2])(implicit ev: ClassTag[T]) = data collect {
case t: T => t
}
例如:
val data = Seq(new B, new B, new C, new B)
filter[B, A](data) //Traversable[B] with length 3
filter[C, A](data) //Traversable[C] with length 1
这样做的一个问题是它可能无法按预期使用嵌套泛型类型。
collect 方法接受PartialFunction 类型的参数,表示不需要在整个域上定义的函数。当使用 collect 未定义 PartialFunction 的元素时,将被过滤掉,并相应地映射与某些 case 语句匹配的元素。
您也可以使用existential types 并让编译器推断data 参数的类型以获得更简洁的语法。你也可以使用context bounds:
def filter[T : ClassTag](data: Traversable[_]) = data collect { case t: T => t }
filter[B](data)
这里的方法的一个问题是,您拥有的本机 filter 方法之间存在显着差异:这些方法总是返回 Traversable,而本机 filter 返回它可以的最佳类型。例如:
val data = Vector(new B, new B, new C, new B)
data filter { _.isInstanceOf[B] } //Vector[A]
data filter { _.isInstanceOf[B] } map { _.asInstanceOf[B] } //Vector[B]
data collect { case t: B => t } //Vector[B]. Note that if you know the type at the calling point, this is pretty concise and might not need a helper method at all
//As opposed to:
filter[B](data) //Traversable[B], not a Vector!
您可以通过使用另一个隐式参数的CanBuildFrom 模式来解决此问题。您也可以使用implicit classes 实质上将方法添加到类中(与上面显示的静态样式调用方法相反)。所有这些加起来是一个相当复杂的方法,但如果您对这些增强功能感兴趣,我将把它留在这里:
implicit class RichTraversable[T2, Repr <: TraversableLike[T2, Repr], That](val trav: TraversableLike[T2, Repr]) extends AnyVal {
def withType[T : ClassTag](implicit bf: CanBuildFrom[Repr, T, That]) = trav.collect {
case t: T => t
}
}
这将允许您这样做:
data.withType[B] //Vector[B], as desired