如何使用 Scala 反射递归访问对象的所有 public 成员?
How to access all public members of an object recursively using Scala reflection?
我找不到如何在没有 knowing/specifying 类型的情况下递归访问对象成员。在我的例子中,问题仅限于我想访问的 lazy val
s 和 object
成员。对象可以嵌套到任何深度,其中包含 lazy vals
。例如:
object TestShallow {
lazy val value1 = 1
}
object TestDeep {
lazy val value1 = 1
object NestedObj {
lazy val value2 = 2
}
}
这是我目前的情况:
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)
}
它适用于一级声明:
scala> evalMemberValues(TestShallow)
INSPECTING: $line7.$read$$iw$$iw$$iw$$iw$TestShallow$@1669f4e5: TestShallow.type
OTHERS: constructor TestShallow
VAL/DEF: lazy value value1 = 1
但是,它无法正确递归:
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 ##
如您所见,问题出在这一行:
val inst = mirror.reflectModule(m.asModule).instance
因为它给了我一个 Any
类型的实例,信息丢失了。理想情况下,我会得到一个实例,其中包含 TypeTag
和 ClassTag
对应于 m
的适当类型的详细信息。我没有找到如何从 Symbol
获取它,这就是 m
是什么,我猜编译器不会生成它。我也不知道如何使用 instanceOf[_]
来转换它。也许我可以通过其他方式获得 declarations/members?我发现的所有示例都没有动态获取实例类型,也没有在实例上递归以获取下一级声明。
此外,检查 Symbol
即 val
或 lazy val
的更好方法是什么?我只在 ModuleSymbol
: isVal
, isLazy
中看到这样的检查,这对我来说有点奇怪。
这对我有用:
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)
}
一些解释:
我使用的是隐式 TypeTag
而不是 ClassTag
,因为 TypeTag
带有方便的 tpe
属性,其中包含有关的完整信息检查的类型。我将此 Type
属性 传递给 loop
方法。
我找不到如何在没有 knowing/specifying 类型的情况下递归访问对象成员。在我的例子中,问题仅限于我想访问的 lazy val
s 和 object
成员。对象可以嵌套到任何深度,其中包含 lazy vals
。例如:
object TestShallow {
lazy val value1 = 1
}
object TestDeep {
lazy val value1 = 1
object NestedObj {
lazy val value2 = 2
}
}
这是我目前的情况:
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)
}
它适用于一级声明:
scala> evalMemberValues(TestShallow)
INSPECTING: $line7.$read$$iw$$iw$$iw$$iw$TestShallow$@1669f4e5: TestShallow.type
OTHERS: constructor TestShallow
VAL/DEF: lazy value value1 = 1
但是,它无法正确递归:
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 ##
如您所见,问题出在这一行:
val inst = mirror.reflectModule(m.asModule).instance
因为它给了我一个 Any
类型的实例,信息丢失了。理想情况下,我会得到一个实例,其中包含 TypeTag
和 ClassTag
对应于 m
的适当类型的详细信息。我没有找到如何从 Symbol
获取它,这就是 m
是什么,我猜编译器不会生成它。我也不知道如何使用 instanceOf[_]
来转换它。也许我可以通过其他方式获得 declarations/members?我发现的所有示例都没有动态获取实例类型,也没有在实例上递归以获取下一级声明。
此外,检查 Symbol
即 val
或 lazy val
的更好方法是什么?我只在 ModuleSymbol
: isVal
, isLazy
中看到这样的检查,这对我来说有点奇怪。
这对我有用:
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)
}
一些解释:
我使用的是隐式 TypeTag
而不是 ClassTag
,因为 TypeTag
带有方便的 tpe
属性,其中包含有关的完整信息检查的类型。我将此 Type
属性 传递给 loop
方法。