How to obtain the raw datatype of a parameter of a field that is specialized in Scala via reflection?

故事扮演 提交于 2019-12-22 20:43:00

问题


I have a class with a field that is specialized and is using a raw datatype. For instance a 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   

I want to use reflection now to find out the type parameters of the Tuple2 inside my 'refl' instance. (I cheat a little using 'head' to get the field because I know it's the only one.)

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

Now that I have the field I can query the generic types.

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

The problem now is that the first type is Object. Is there a way to know, via reflection alone, the real type (Int) of that parameter?

Update:

I'm using this in a context of automatic serialization within my own API. Given a class marked with @Serializable I can serialize it. To do so I must build a tree of the fields and types of the class recursively using reflection so I can do a deep serialization.

If I'm working directly with a @Specialized class it works because the types are explicit and known at compile time at the invocation site. If a field in the hierarchy is @specialized I have no way to tell via reflection. Querying the fields or methods declared in a class doesn't yield the correct value. The type is present in runtime but only on the instance held in the field, not on the declaration of the field itself. Thus if the instance is null and can't do a "getClass" I can't know the correct types by reflection alone.


回答1:


The Problem is you are using a Java reflection API which doesn't workaround the JVM's "Type Erasure" issue, because of that there's no way to find out the actual generic types with it.

Fortunately the coming 2.10 version of Scala implements new reflection API, which solves the type erasure issue. But since 2.10 hasn't been released yet, the API isn't standardized nor documented yet. Your best bet is to dig into it with some tool like debugger and ask more specific questions that come up here.

In Scala 2.10-M5 you can access the API like follows:

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

Update #1

The following function shows how you can get a type of an instance:

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)]

In fact it's all based around the [T : TypeTag] part which tells the compiler to magically create an implicit instance of the reflection to the type passed.

Update #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:


I found a way but it's not pretty.

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

I created the annotation specializedFor with runtime retention policy. It receives an Array of Class[_]. That way I can find in runtime exclusively with reflection the types of the Tuple2 field.

It's unsafe because I can't test that the Array contains the same types as the Tuple2.

In my API I have to first check if the annotation is present and if it is force the genericTypes to be those.



来源:https://stackoverflow.com/questions/11586944/how-to-obtain-the-raw-datatype-of-a-parameter-of-a-field-that-is-specialized-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!