In this article, I will explore how to get the value of vals, vars and methods with no arguments using reflection. I’ll present three solutions for this problem: using Java reflection, using Scala reflection and using Scala macros (compile-time reflection).

The function I want to implement takes two arguments: an object and the name of the member for which the value should be retrieved.

def getValue[T](obj: T, memberName: String): Any = ???

As I started exploring this problem in the context of a validation library, the most important use case is getting the value for the fields of a case class.

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

val p = Person("John Doe", 31)

getValue(p, "name")
getValue(p, "age")

However, the implementation also works with regular classes and it can obtain the value for vals, vars and methods that are declared in the class itself or inherited.

class Person(val firstName: String, val lastName: String, val age: Int) {
  var index = 0

  def fullName = firstName + " " + lastName

  def show(): String = {
    println(firstName)
    firstName
  }
}

val p = new Person("John", "Doe", 31)

getValue(p, "firstName")
getValue(p, "index")
getValue(p, "fullName")
getValue(p, "show")
getValue(p, "toString")

Note: no error handling is implemented in order to keep the code simple. The implementations are behaving differently for invalid input.

Java Reflection

The implementation using Java reflection is pretty straight forward. It obtains a Method object by name and invokes it on obj.

def getValue[T](obj: T, memberName: String): Any = {
  val method = obj.getClass.getMethod(memberName)
  method.invoke(obj)
}

What might come as a surprise is that we are searching for methods, even though the implementation should also work with vals and vars. This is because Scala is generating default accessors for any val or var, except the ones that are defined as private[this] (for vars, mutators are also generated).

Scala Reflection

Using Scala reflection leads to an implementation which is similar to the one based on Java reflection API. One important difference is that it is relying on implicit type tags and class tags generated by the compiler in order to preserve additional type info for erased types and types which are specific to Scala.

import scala.reflect.ClassTag
import scala.reflect.runtime.universe._

def getValue[T: TypeTag : ClassTag](obj: T, memberName: String): Any = {
  val symbol = typeOf[T].member(TermName(memberName)).asMethod

  val m = runtimeMirror(obj.getClass.getClassLoader)
  val im = m.reflect(obj)

  im.reflectMethod(symbol).apply()
}

The TypeTag context bound is needed when executing typeOf[T] and the ClassTag one is used by m.reflect(obj).

Scala Macros

As macros are executed at compile time, the code doesn’t know what values are passed as arguments. The macro has to generate code that knows how to return the value for any member name. The code that is generated for the Person case class looks like the following. I’m constructing a map of functions and not actual values because I want methods to be executed only when the value is needed (I want to avoid executing methods with side-effects or methods that are performing complex computations).

def getValue(p: Person, memberName: String): Any = {
  val membersMap = Map("name" -> p.name _, "age" -> p.age _)
  val fn = membersMap(memberName)
  fn()
}

In order to achieve this result for a generic type, the code selects all the public methods with no parameters or type arguments that are not constructors. Once it has a list with these members, it constructs a map that binds the member name to a function that returns the value of the member. I’m using member.name.decodedName in order to replace all occurrences of $op_names by corresponding operator symbols (foo_$plus$eq decodes to foo_+=)

import scala.reflect.macros.blackbox
import scala.language.experimental.macros

def getValue[T](obj: T, memberName: String): Any = macro impl[T]

def impl[T: c.WeakTypeTag](c: blackbox.Context)(obj: c.Tree, memberName: c.Tree): c.Tree = {
  import c.universe._

  val selectedMembers = weakTypeOf[T].members.filter { member =>
    member.isPublic && member.isMethod &&
      !member.isConstructor && !member.typeSignature.takesTypeArgs &&
      (member.typeSignature.paramLists.isEmpty || member.typeSignature.paramLists.head.isEmpty)
  }

  val mappings = selectedMembers.map { member =>
    q"${member.name.decodedName.toString} -> $obj.$member _"
  }

  q"""
    val membersMap = Map(..$mappings)
    val fn = membersMap($memberName)
    fn()
  """
}