【问题标题】:How to obtain the raw datatype of a parameter of a field that is specialized in Scala via reflection?如何通过反射获取Scala专用的字段参数的原始数据类型?
【发布时间】:2012-07-20 04:17:02
【问题描述】:

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

【问题讨论】:

    标签: scala reflection specialized-annotation


    【解决方案1】:

    问题是您使用的 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)
    

    【讨论】:

    • 你确定它在不依赖实例的情况下工作吗?在 2.9 中:scala&gt; classManifest[(Int, String)] res11: ClassManifest[(Int, String)] = scala.Tuple2[Int, java.lang.String] 这很有效,因为我正在提供实例。你能用反射找出类中真正的字段类型吗?
    • 我仍然不确定 sn-p 是否能证明这一点。您示例中的类型在编译时解析。我也可以通过这种方式获得 2.9 中的信息。在我的情况下,我需要在一个类中搜索一个字段,在运行时我不知道它是什么。尝试创建class TupleReflection(val tuple: Tuple2[Int, String]) ,看看你是否能确定元组字段的类型。
    • @TomasLazaro 你在说什么?当然,类型是在编译时解析的——Scala 是一种静态语言,你知道的。当您为其提供了特定类型Tuple2[Int, String] 时,您为什么对您的tuple 毫无头绪?现在,如果您将其设置为Any,情况将有所不同。很抱歉,但我只是不明白你在这里想要达到的目标,试着在你的问题中解释你需要什么。对于它的价值,我已经用你问我的例子更新了答案。
    • 那么您的解决方案是正确的。我更新了我的问题,我希望它清楚为什么我不能得到 Tuple2 的类型,至少在 Scala 2.9 中。否则请告诉我。谢谢!
    【解决方案2】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多