3

我已阅读TypeTag相关文章,但无法实现按元素类型过滤集合。

例子:

trait A
class B extends A
class C extends A

val v = Vector(new B,new C)
v filter ( _.isInstanceOf[B] )

上面的代码工作正常。但是我想从中提取filter. v例如

def filter[T,T2](data:Traversable[T2]) = (data filter (  _.isInstanceOf[T])).asInstanceOf[Traversable[T]]

//Then filter v by
filter[B,A](v)

在这种情况下,我会收到警告abstract type T is unchecked since it is eliminated by erasure。我尝试使用TypeTag,但运行时似乎并不容易Type

是否有任何优雅的解决方案来实现功能filter?通过 scala 宏的任何解决方案也是可以接受的。

4

2 回答 2

7

您需要提供 a ClassTag,而不是 a TypeTag,并使用模式匹配。 ClassTags与模式匹配配合得很好。您甚至可以使用该collect方法来执行filterandmap一起:

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语句匹配的元素。

您还可以使用存在类型并让编译器推断data参数的类型以获得更简洁的语法。您还可以使用上下文边界

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 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    
于 2015-04-27T03:16:11.490 回答
0

肯定有更简单的方法。下面的作品,但我什至希望有一个更简单的解决方案

trait A
case class B()
case class C()
import scala.reflect.runtime.universe._
import scala.util.Try
val m = runtimeMirror(getClass.getClassLoader)
def filter[T](data:Traversable[_])(implicit t:TypeTag[T]) = data collect {
      case i if Try(i.getClass.asSubclass(m.runtimeClass(typeOf[T].typeSymbol.asClass))).isSuccess => i.asInstanceOf[T]
    }
val v= Vector(new B,new C)

scala>     println(filter[B](v))
Vector(B()) //This gets printed
于 2015-04-27T03:13:30.937 回答