Scala 2.10 reflection experiments

Scala 2.10.0 is now officially available and includes a new reflection API. The relevant section of the official documentation is still largely missing though, and the feature is marked as “experimental” – with thread-safety being a known issue.

To take it for a spin, I wrote a simple library to serialize and deserialize objects in JSON format, using reflection to auto-detect field names and types. The code is available at Github as Seriala.

This is a quick summary by examples of what I learnt. First of all to use reflection you need to add scala-reflect.jar (in addition to scala-library.jar) to your classpath, and add these imports

import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror

Given some class, e.g.

case class Person(name: String, age: Int)

here’s how to get its Type, constructor, and name and type of each of the constructor arguments

scala> val personType = typeOf[Person]
personType: reflect.runtime.universe.Type = Person

scala> val ctor = personType.member(nme.CONSTRUCTOR)
ctor: reflect.runtime.universe.Symbol = constructor Person

scala> val args = ctor.asMethod.paramss.head map { p => (p.name.decoded, p.typeSignature) }
args: List[(String, reflect.runtime.universe.Type)] = List((name,String), (age,scala.Int))

Besides typeOf, it’s also possible to access a Type using a TypeTag, which is particularly useful when implicitly defined in a method. The following method returns name and type of each constructor argument as above, but in a reusable function

scala> def ctorArgs[T](implicit ttag: TypeTag[T]) =
     |     ttag.tpe.member(nme.CONSTRUCTOR).asMethod.paramss.head map {
     |       p => (p.name.decoded, p.typeSignature)
     |     }
ctorArgs: [T](implicit ttag: reflect.runtime.universe.TypeTag[T])List[(String, reflect.runtime.universe.Type)]

scala> ctorArgs[Person]
res2: List[(String, reflect.runtime.universe.Type)] = List((name,String), (age,scala.Int))

To invoke a method, such as the constructor to create a new instance of the Person class, you need the mirror

scala> val classMirror = currentMirror.reflectClass(personType.typeSymbol.asClass)
classMirror: reflect.runtime.universe.ClassMirror = class mirror for Person (bound to null)

scala> val ctor = personType.declaration(nme.CONSTRUCTOR).asMethod
ctor: reflect.runtime.universe.MethodSymbol = constructor Person

scala> classMirror.reflectConstructor(ctor).apply("Joe", 73)
res1: Any = Person(Joe,73)

Unlike Java, where the type of generic collections is erased and not available at runtime, reflection in Scala retains type parameters

scala> val personListType = typeOf[List[Person]]
personListType: reflect.runtime.universe.Type = scala.List[Person]

However the Type doesn’t currently expose such parameters; the workaround is to cast it to a TypeRefApi

scala> personListType.args
<console>:15: error: value args is not a member of reflect.runtime.universe.Type
              personListType.args
                             ^
scala> personListType.asInstanceOf[TypeRefApi].args
res5: List[reflect.runtime.universe.Type] = List(Person)

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s