如何通过反射获取专门用于Scala的字段参数的原始数据类型?

时间:2012-07-20 21:06:14

标签: scala reflection specialized-annotation

我有一个专门的字段并使用原始数据类型的类。例如,Tuple2 [Int,String]:

scala> class TupleReflection(val tuple: Tuple2[Int, String])
defined class TupleReflection

scala> val refl = new TupleReflection((5, "hello"))
refl: TupleReflection = TupleReflection@1a597ec8   

我想现在使用反射来找出我的'refl'实例中的Tuple2的类型参数。 (我用'头'来欺骗这个领域,因为我知道这是唯一一个。)

scala> val field = refl.getClass.getDeclaredFields.head
field: java.lang.reflect.Field = private final scala.Tuple2 TupleReflection.tuple

现在我有了这个字段,我可以查询泛型类型。

scala> field.getGenericType
res41: java.lang.reflect.Type = scala.Tuple2<java.lang.Object, java.lang.String>

现在的问题是第一个类型是Object。有没有办法通过单独的反射知道该参数的实际类型(Int)?

更新

我在自己的API中自动序列化的上下文中使用它。给定一个标有@Serializable的类,我可以序列化它。为此,我必须使用反射递归地构建类的字段和类型的树,以便我可以进行深度序列化。

如果我直接使用@Specialized类,它可以工作,因为这些类型是显式的,并且在编译时在调用站点处是已知的。如果层次结构中的字段是@specialized,我无法通过反射来判断。查询类中声明的字段或方法不会产生正确的值。类型存在于运行时,但仅存在于字段中保存的实例上,而不是字段本身的声明上。因此,如果实例为null并且不能执行“getClass”,我无法仅通过反射知道正确的类型。

2 个答案:

答案 0 :(得分:5)

问题是你正在使用Java反射API,它不能解决JVM的“类型擦除”问题,因为没有办法找出它的实际泛型类型。

幸运的是,即将推出的2.10版Scala实现了新的反射API,它解决了类型擦除问题。但由于尚未发布2.10,因此API尚未标准化,尚未记录。最好的办法是使用调试器之类的工具深入研究它,并询问更多具体问题。

在Scala 2.10-M5中,您可以访问以下API:

scala> reflect.runtime.universe.typeOf[(Int, String)]
res0: reflect.runtime.universe.Type = (Int, String)

scala> reflect.runtime.universe.typeOf[(Int, String)].typeArguments
res1: List[reflect.runtime.universe.Type] = List(Int, String)

scala> reflect.runtime.universe.typeOf[(Int, String)].typeArguments.head
res2: reflect.runtime.universe.Type = Int

更新#1

以下函数显示了如何获取实例的类型:

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> def typeOf[T : TypeTag](x : T) = reflect.runtime.universe.typeOf[T]
typeOf: [T](x: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type

scala> typeOf(Seq((1,"sldf"),(20,"sldkfjew")))
res0: reflect.runtime.universe.Type = Seq[(Int, String)]

实际上它都是基于[T : TypeTag]部分,它告诉编译器神奇地为传递的类型创建一个隐式反射实例。

更新#2

scala> class TupleReflection(val tuple: Tuple2[Int, String])
defined class TupleReflection

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> typeOf[TupleReflection].member(newTermName("tuple")).typeSignature
res6: reflect.runtime.universe.Type = => (scala.Int, String)

scala> typeOf[TupleReflection].members
res7: Iterable[reflect.runtime.universe.Symbol] = List(constructor TupleReflection, value tuple, value tuple, method $asInstanceOf, method $isInstanceOf, method synchronized, method ##, method !=, method ==, method ne, method eq, constructor Object, method notifyAll, method notify, method clone, method getClass, method hashCode, method toString, method equals, method wait, method wait, method wait, method finalize, method asInstanceOf, method isInstanceOf, method !=, method ==)

scala> typeOf[TupleReflection].members.view.filter(_.isValue).filter(!_.isMethod).toList
res16: List[reflect.runtime.universe.Symbol] = List(value tuple)

答案 1 :(得分:0)

我找到了一种方法,但它并不漂亮。

class TupleReflection(@(specializedFor @field)(Array(classOf[Int], classOf[String]) val tuple: Tuple2[Int, String])

我使用运行时保留策略创建了注释dedicatedFor。它接收一个Class [_]数组。这样我就可以在运行时专门用反射来查找Tuple2字段的类型。

这是不安全的,因为我无法测试Array包含与Tuple2相同的类型。

在我的API中,我必须首先检查是否存在注释,以及是否强制使用genericTypes。