Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala reflection to access all public fields at runtime

Tags:

scala

Given the following class hierarchy:

class A {
  val x = 3
  val y = 4
}

class B extends A {
  val z = 5
}

And say I have an instance of B with no compile-time knowledge of its class hierarchy and would like to be able to report something like the following:

Class: B
val[x]: 3
val[y]: 4
val[z]: 5

What would be the best approach using Scala reflection?

Thanks Des

like image 364
user79074 Avatar asked Jan 30 '14 19:01

user79074


2 Answers

In brief:

import scala.reflect.runtime.universe._

val a = new A
val rm = scala.reflect.runtime.currentMirror
val accessors = rm.classSymbol(a.getClass).toType.members.collect {
  case m: MethodSymbol if m.isGetter && m.isPublic => m 
}
val instanceMirror = rm.reflect(a)
for(acc <- accessors)
  println(s"$a: ${instanceMirror.reflectMethod(acc).apply()}")
like image 64
J Cracknell Avatar answered Oct 10 '22 01:10

J Cracknell


You can you TypeTag to get an access:

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> class A {
     |   val x = 3
     |   val y = 4
     | }
defined class A

scala> class B extends A {
     |   val z = 5
     | }
defined class B

scala> typeOf[B]
res0: reflect.runtime.universe.Type = B

Now you have two options: members which shows all members (even inherited ones) and declarations (only defined in the current class), e.g:

scala> res0.declarations
res2: reflect.runtime.universe.MemberScope = SynchronizedOps(constructor B, value z, value z)

If you want to work with this field you need reflect them through InstanceMirror:

scala> val b = new B
b: B = B@6ebe10dd

scala> val currentMirror = runtimeMirror(getClass.getClassLoader)
.....

scala> val bMir = currentMirror.reflect(b)
bMir: reflect.runtime.universe.InstanceMirror = instance mirror for B@6ebe10dd

Now you just need to get a Symbol you need, e.g you want to get a value of z varible:

scala> val zt = typeOf[B].declaration("z": TermName).asMethod
zt: reflect.runtime.universe.MethodSymbol = value z

scala> bMir.reflectField(zt).get
res20: Any = 5
like image 26
4lex1v Avatar answered Oct 10 '22 03:10

4lex1v