I can't find how to access object members RECURSIVELY without knowing/specifying their types. In my case the problem is limited to lazy val
s and object
members that I want to access. Objects can be nested to any depth with lazy vals
present in them. For example:
object TestShallow {
lazy val value1 = 1
}
object TestDeep {
lazy val value1 = 1
object NestedObj {
lazy val value2 = 2
}
}
Here is what I have so far:
import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
def evalMemberValues[A: TypeTag](topLevelObj: A)(implicit c: ClassTag[A]): Unit = {
val mirror = runtimeMirror(getClass.getClassLoader)
def loop[B: TypeTag](obj: B)(implicit c: ClassTag[B]): Unit = {
println(s"INSPECTING: $obj: ${typeOf[B]}")
val members = typeOf[B].decls.filter(_.isPublic)
members.foreach { m =>
if(m.isTerm && m.isModule) {
println(s"MODULE: $m")
// THE PROBLEM IS HERE !!!:
val inst = mirror.reflectModule(m.asModule).instance // type is Any
loop(inst)
}
else if(m.isTerm && ! m.isConstructor && m.isMethod && m.typeSignature.paramLists.isEmpty && ! m.typeSignature.takesTypeArgs) {
val im = mirror.reflect(obj)
val value = im.reflectMethod(m.asMethod)()
println(s"VAL/DEF: $m = $value")
}
else {
println(s"OTHERS: $m")
}
}
}
loop(topLevelObj)
}
It works fine for first level declarations:
scala> evalMemberValues(TestShallow)
INSPECTING: $line7.$read$$iw$$iw$$iw$$iw$TestShallow$@1669f4e5: TestShallow.type
OTHERS: constructor TestShallow
VAL/DEF: lazy value value1 = 1
However, it fails to recurse properly:
scala> evalMemberValues(TestDeep)
INSPECTING: $line11.$read$$iw$$iw$$iw$$iw$TestDeep$@3c2f310c: TestDeep.type
OTHERS: constructor TestDeep
VAL/DEF: lazy value value1 = 1
MODULE: object NestedObj
INSPECTING: $line11.$read$$iw$$iw$$iw$$iw$TestDeep$NestedObj$@4f1f2f84: Any
OTHERS: method ==
OTHERS: method !=
OTHERS: method equals
OTHERS: method hashCode
OTHERS: method toString
OTHERS: method getClass
OTHERS: method isInstanceOf
OTHERS: method asInstanceOf
OTHERS: method ##
As you can see the problem is with this line:
val inst = mirror.reflectModule(m.asModule).instance
because it gives me an instance of type Any
and information is lost. Ideally I would get an instance with TypeTag
and ClassTag
details of a proper type that corresponds to m
. I didn't find how to get that from Symbol
, which is what m
is, I guess compiler won't generate that. I also don't see how to cast it using instanceOf[_]
. Maybe I could get declarations/members in some other way? All examples I've found don't get instance type dynamically and don't recurse on the instance to get next level declarations.
Additionally, what's a better way to check for Symbol
that is val
or lazy val
? I only see such checks in ModuleSymbol
: isVal
, isLazy
which is kind of strange to me.
This works for me:
import scala.reflect.runtime.universe._
def evalMemberValues[A](topLevelObj: A)(implicit c: TypeTag[A]): Unit = {
val mirror = runtimeMirror(getClass.getClassLoader)
def loop(obj: Any, tp: Type): Unit = {
println(s"INSPECTING: $tp:")
val objMirror = mirror.reflect(obj)
val members = tp.decls.filter(_.isPublic)
members.foreach { m =>
if (m.isTerm && m.isModule) {
println(s"MODULE: $m")
loop(mirror.reflectModule(m.asModule).instance, m.info)
}
else if (m.isTerm && !m.isConstructor && m.isMethod && m.typeSignature.paramLists.isEmpty && !m.typeSignature.takesTypeArgs) {
val value = objMirror.reflectMethod(m.asMethod)()
println(s"VAL/DEF: $m = $value")
}
else {
println(s"OTHERS: $m")
}
}
}
loop(topLevelObj, c.tpe)
}
Some explanation:
I am using an implicit TypeTag
instead of a ClassTag
, because TypeTag
comes with the convenient tpe
property, which contains full information about the inspected type. I pass this Type
property down to the loop
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With