2

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.

Charles
  • 50,943
  • 13
  • 104
  • 142
Tomas Lazaro
  • 315
  • 1
  • 8

2 Answers2

5

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)
Community
  • 1
  • 1
Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169
  • Are you sure it works without depending on an instance? In 2.9: `scala> classManifest[(Int, String)] res11: ClassManifest[(Int, String)] = scala.Tuple2[Int, java.lang.String]` That works because I'm providing the instance. Can you find out the real types of field inside a class using reflection? – Tomas Lazaro Jul 20 '12 at 22:21
  • I'm still not sure that snippet proves it. The types in your example are resolved at compile time. I can have the information in 2.9 that way too. In my case I need to search for a field inside a class which I have no clue what it during runtime. Try creating `class TupleReflection(val tuple: Tuple2[Int, String]) ` and see if you can figure the types of the tuple field. – Tomas Lazaro Jul 21 '12 at 01:10
  • @TomasLazaro What are you talking about? Of course, the types are resolved at compile time - Scala is a static language, you know. And how come you have no clue about your `tuple` when you have provided a specific type `Tuple2[Int, String]` for it? Now if you had it set to `Any`, this would be a different case. I'm sorry but I just don't get what you're trying to achieve here, try to explain what you need that all for in your question. For what it's worth I've updated the answer with example you've asked me for. – Nikita Volkov Jul 21 '12 at 01:59
  • Your solution is correct then. I updated my question, I hope it makes it clear why I can't get the type of the Tuple2, at least in Scala 2.9. Otherwise please let me know. Thanks! – Tomas Lazaro Jul 21 '12 at 02:34
0

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.

Tomas Lazaro
  • 315
  • 1
  • 8