How to Start Using the Scala Reflection Api
I’ve found learning the scala reflection API somewhat confusing. Having used Java reflection pretty easily back in the day, the Scala api seemed somewhat foreign.
So let’s start with a simple exercise to learn how to use the api.
A Simple Exercise
How would we go about using the scala reflection api to find out what methods are declared on a type?
1. Import the reflection universe
First, you need to import the reflection runtime universe:
import scala.reflect.runtime.universe._
Most methods on the reflection api are centred around a Type:
.runtime.universe.Type reflect
I’ll refer to Type as universe.Type from now on, to distinguish it from a normal type.
2. Get the universe.Type
To get the universe.Type of a type, you can use the typeOf api method:
[Option[_]]
typeOf: reflect.runtime.universe.Type = scala.Option[_] res1
3. Get the declared methods
Now that we have a universe.Type for our type, we can get the methods defined on it by using the decls method:
.decls
res1: reflect.runtime.universe.MemberScope = SynchronizedOps(constructor Option, method isEmpty, method isDefined, method get, method getOrElse, method orNull, method map, method fold, method flatMap, method flatten, method filter, method filterNot, method nonEmpty, method withFilter, class WithFilter, method contains, method exists, method forall, method foreach, method collect, method orElse, method iterator, method toList, method toRight, method toLeft) res9
You might notice that decls returns a MemberScope. What’s that? It’s handy to realise that a MemberScope is a Traversable:
.isInstanceOf[Traversable[_]]
res9: Boolean = true res12
You can use the methods available on any Traversable instance to process the MemberScope.
For instance, we could easily format the list of method declarations like so:
.decls.mkString("\n")
res1: String =
res11def <init>(): Option[A]
def isEmpty: Boolean
def isDefined: Boolean
def get: A
final def getOrElse[B >: A](default: => B): B
final def orNull[A1 >: A](implicit ev: <:<[Null,A1]): A1
final def map[B](f: A => B): Option[B]
final def fold[B](ifEmpty: => B)(f: A => B): B
final def flatMap[B](f: A => Option[B]): Option[B]
def flatten[B <: <?>](implicit ev: <?>): Option[B]
final def filter(p: A => Boolean): Option[A]
final def filterNot(p: A => Boolean): Option[A]
final def nonEmpty: Boolean
final def withFilter(p: A => Boolean): Option.this.WithFilter
class WithFilter extends AnyRef
final def contains[A1 <: <?>](elem: <?>): Boolean
final def exists(p: A => Boolean): Boolean
final def forall(p: A => Boolean): Boolean
final def foreach[U](f: A => U): Unit
final def collect[B](pf: PartialFunction[A,B]): Option[B]
final def orElse[B >: A](alternative: => Option[B]): Option[B]
def iterator: Iterator[A]
def toList: List[A]
final def toRight[X](left: => X): Product with Serializable with scala.util.Either[X,A]
final def toLeft[X](right: => X): Product with Serializable with scala.util.Either[A,X]
Other useful methods
Let’s use the reflection api to figure out what other methods are available on universe.Type. We use the members method to list methods defined either directly or indirectly on universe.Type:
[Type]
typeOf: reflect.runtime.universe.Type = scala.reflect.runtime.universe.Type
res5
.members.mkString("\n")
res5: String =
res7final def ##(): Int
def contains(sym: <?>): Boolean
def exists(p: <?>): Boolean
def find(p: <?>): Option[Types.this.Type]
def foreach(f: <?>): Unit
def map(f: <?>): Types.this.Type
def substituteTypes(from: <?>,to: <?>): Types.this.Type
def substituteSymbols(from: <?>,to: <?>): Types.this.Type
def orElse(alt: <?>): Types.this.Type
def finalResultType: Types.this.Type
def resultType: Types.this.Type
def typeParams: List[Types.this.Symbol]
def paramLists: List[List[Types.this.Symbol]]
def paramss: List[List[Types.this.Symbol]]
def typeArgs: List[Types.this.Type]
def dealias: Types.this.Type
def widen: Types.this.Type
def erasure: Types.this.Type
def asSeenFrom(pre: <?>,clazz: <?>): Types.this.Type
def baseType(clazz: <?>): Types.this.Type
def baseClasses: List[Types.this.Symbol]
def =:=(that: <?>): Boolean
def weak_<:<(that: <?>): Boolean
def <:<(that: <?>): Boolean
def etaExpand: Types.this.Type
def normalize: Types.this.Type
def typeConstructor: Types.this.Type
def takesTypeArgs: Boolean
def companion: Types.this.Type
def members: Types.this.MemberScope
def member(name: <?>): Types.this.Symbol
def decls: Types.this.MemberScope
def declarations: Types.this.MemberScope
def decl(name: <?>): Types.this.Symbol
def declaration(name: Types.this.Name): Types.this.Symbol
def typeSymbol: Types.this.Symbol
def termSymbol: Types.this.Symbol
Getting a universe.Type from an Instance
What if you have an instance of a type and want to get a universe.Type for that? It looks like there is no built in method to do that. The recommended way is to write your own method for it:
def getType[T: TypeTag](obj: T) = typeOf[T]
: [T](obj: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type getType
The scala compiler will supply our getType method with an implicit for TypeTag[T].
So What is a TypeTag?
A TypeTag[T] encapsulates the runtime type representation of some type T. Like scala.reflect.Manifest, the prime use case of TypeTags is to give access to erased types.
As with Java, Scala generic types which are present at compile time are erased at runtime (erasure). TypeTags are a way of having access to that lost compile time information at runtime.
With getType we can now extract the universe.Type of an instance:
getType(List(1,2,3))
: reflect.runtime.universe.Type = List[Int] res4
Hopefully this has given you a taste for some of the information provided by the scala reflection api and a starting point to explore it further.