4e6*_*4e6 10 reflection scala scala-2.10
我正在挖掘新的scala反射api,并且无法弄清楚为什么以下代码段不能按预期工作.给定层次结构(试图尽可能地简化):
import scala.reflect.runtime.universe._
trait TF[A] {
implicit def t: TypeTag[A]
def f[T <: A: TypeTag]: PartialFunction[Any, A] = {
case msg: T if typeOf[T] =:= typeOf[A] => msg
}
}
class TFilter[T: TypeTag] extends TF[T] {
def t = typeTag[T]
}
case class Foo(x: Int)
Run Code Online (Sandbox Code Playgroud)
我希望方法f过滤给定类型的对象.所以下面的片段应该返回Seq[Foo]
val messages = Seq(1, "hello", Foo(1))
val tFilter = new TFilter[Foo]
messages collect tFilter.f[Foo]
Run Code Online (Sandbox Code Playgroud)
它实际上会返回,Seq[Foo]但其他消息未经过滤,这听起来像一个错误.
res1: Seq[Foo] = List(1, hello, Foo(1))
Run Code Online (Sandbox Code Playgroud)
问题.我使用TypeTag错误还是新反射api的缺陷?
PS0.试过Scala 2.10.0-RC1和2.10.0-RC2
PS1.解决方法是替换TypeTag为Manifest,因此collect序列上的以下代码List(Foo(1))将按预期返回.
trait MF[A] {
implicit def m: Manifest[A]
def f[T <: A: Manifest]: PartialFunction[Any, A] = {
case msg: T if typeOf[T] =:= typeOf[A] => msg
}
}
class MFilter[T: Manifest] extends MF[T] {
def m = manifest[T]
}
Run Code Online (Sandbox Code Playgroud)
更新:与新Scala 2.10.0-RC2版本相同.
所以我认为这里的关键问题是你需要匹配类型msg,但它的编译时类型是Any(来自PartialFunction声明).从本质上讲,你需要TypeTag为你的每个元素添加一个不同的元素List[Any].但是因为它们都具有编译时类型,Any因为所有这些都被放入同一个列表中,所以你不会得到一个TypeTag比这更具体的东西.
我想你可能想做的是用ClassTag而不是TypeTag:
trait TF[A] {
implicit def t: ClassTag[A]
def f: PartialFunction[Any, A] = {
case msg: A => msg
}
}
class TFilter[T: ClassTag] extends TF[T] {
def t = classTag[T]
}
case class Foo(x: Int)
val messages = Seq(1, "hello", Foo(1), List(1), List("a"))
messages collect new TFilter[Foo].f // produces List(Foo(1))
Run Code Online (Sandbox Code Playgroud)
正如Ajran所指出的那样,就像Manifest版本一样,你必须要了解运行时类型的所有限制,包括擦除和装箱问题:
messages collect new TFilter[List[Int]].f // produces List(List(1), List("a"))
messages collect new TFilter[Int].f // produces List()
messages collect new TFilter[java.lang.Integer].f // produces List(1)
Run Code Online (Sandbox Code Playgroud)
关于如何使TypeTag模式匹配更有用的一些建议(例如SI-6517),但我认为只有当你用一个有用的对象匹配时才有用TypeTag,而不是一个编译时类型的对象Any.