如何按类型参数化过滤Scala对象的集合?

时间:2013-06-03 18:25:44

标签: scala reflection types

给定包含变量类型项的Scala集合,我可以按类型过滤。

trait X
case class Y(y:Int) extends X
case class Z(z:Int) extends X
val l = List(Y(1), Y(2), Z(3), Z(4))
l.collect{case e: Y=>e} // returns List[Y] = List(Y(1), Y(2))
l.collect{case e: Z=>e} // returns List[Z] = List(Z(3), Z(4))

我需要参数化过滤。

val f = Y
l.collect{case e: f=>e} // should return List[Y] = List(Y(1), Y(2))

最后一行返回error: not found: type f。在case语法中不允许参数化Scala类型。

是否有Scala-esque方法来参数化此过滤操作? (也许使用collect函数之外的其他东西。)是否需要反射?

2 个答案:

答案 0 :(得分:1)

val f = Y Y不是类型,而是伴随对象。

您可以使用以下类型:

type T = Y
l.collect{case e: T=>e} // returns List[Y] = List(Y(1), Y(2))

或者您可以使用伴侣对象,但仅限于某些参数计数:

val t = Y
l.collect{case e @ t(_)=>e} // returns List[Y] = List(Y(1), Y(2))

在这种情况下,您应该e @ t(_, _)使用case class Y(y1:Int, y2:Int)e @ t(_, _, _)使用case class Y(y1:Int, y2:Int, y3:Int),依此类推。

答案 1 :(得分:1)

您不能将类型本身分配给val,但可以使用方法类型参数进行参数化:

def filterOnType[T : ClassTag](c: Traversable[Any]): Traversable[T] = {
  val tag = implicitly[ClassTag[T]];
  c.collect { case tag(t) => t } 
}

ClassTag用于在运行时保留有关T的类信息。否则T将被删除,.collect { case t: T => t }将与.collect { case t: AnyRef => t }相同。

scala> filterOnType[Y](l)
res7: Traversable[Y] = List(Y(1), Y(2))

scala> filterOnType[Z](l)
res8: Traversable[Z] = List(Z(3), Z(4))

如果你绝对需要在val中保留一些类型的表示,那么保留ClassTag就可以了。 val必须隐含filterOnlyType才能被filterOnlyType[X](l)(myClassTagForX)提取,或者您可以直接提供:{{1}}。