【问题标题】:How to access all public members of an object recursively using Scala reflection?如何使用Scala反射递归访问对象的所有公共成员?
【发布时间】:2019-05-28 22:29:29
【问题描述】:

如果不知道/指定对象成员的类型,我无法找到如何递归访问对象成员。就我而言,问题仅限于我想要访问的lazy vals 和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 类型的实例并且信息丢失了。理想情况下,我会得到一个具有TypeTagClassTag 详细信息的实例,这些详细信息对应于m。我没有找到如何从Symbol 获得它,这就是m 是什么,我猜编译器不会生成它。我也看不到如何使用instanceOf[_] 进行投射。也许我可以通过其他方式获得声明/成员?我发现的所有示例都没有动态获取实例类型,也没有在实例上递归来获取下一级声明。

此外,检查Symbolvallazy val 的更好方法是什么?我只在ModuleSymbolisValisLazy 中看到这样的检查,这对我来说有点奇怪。

【问题讨论】:

    标签: scala recursion reflection types mirror


    【解决方案1】:

    这对我有用:

      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 方法。

    【讨论】:

    • 谢谢@ygor,很有意义。我试过tpe 但不知何故放弃了它。完美运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2011-09-10
    相关资源
    最近更新 更多