2

我有一个类,该类具有专门的字段并且使用原始数据类型。例如 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 的类型参数。(我用'head'作弊来获得这个领域,因为我知道它是唯一的。)

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,我无法通过反射来判断。查询类中声明的字段或方法不会产生正确的值。该类型存在于运行时,但仅存在于字段中保存的实例上,而不存在于字段本身的声明中。因此,如果实例为空并且不能执行“getClass”,我无法仅通过反射知道正确的类型。

4

2 回答 2

5

问题是您使用的 Java 反射 API 无法解决 JVM 的“类型擦除”问题,因为无法使用它找出实际的泛型类型。

幸运的是,即将到来的 Scala 2.10 版本实现了新的反射 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)
于 2012-07-20T21:21:51.137 回答
0

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

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

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

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

在我的 API 中,我必须首先检查注释是否存在以及是否强制 genericTypes 成为那些。

于 2012-07-21T02:09:31.830 回答