7

我有一个看起来像这样的对象:

object Settings {
  final val Host = "host"
  final val Protocol = "protocol"

  object User {
    final val Name = "username"
    final val Password = "password"
  }

  object Subject {
    final val Query = "query"
    final val Predicate = "predicate"
  }
}

我想做的是类似于membersAsHash(classOf[CollectionSettings])并接收我在对象中声明的所有 val 的哈希值:

[
  Host => "host", 
  Protocol => "protocol", 
  Name => "username",
  Password => "password",
  Query => "query",
  Predicate => "predicate"
]

如果密钥是一个字符串,甚至是完整的包名(例如 com.example.Settings.User),也可以。我真正需要的是价值观,所以如果我只能得到它,它仍然是可以接受的。

这让我得到了子对象的名称,但我似乎无法弄清楚如何获取每个内部的 val:

val optionsToCheck = {
  import scala.reflect.runtime.{universe => ru}
  val mirror = ru.runtimeMirror(getClass.getClassLoader)
  val subObjects = ru.typeOf[CollectionSettings.type].declarations.filter(_.isModule)
  subobjects.map(o => mirror.reflectModule(o.asModule).instance.asInstanceOf[Object].toString).toList
}
4

1 回答 1

11

这里的巧妙之处在于您使用的是常量值定义(即,没有类型注释的最终值;请参阅语言规范的 §4.1 ),因此您甚至不需要任何镜像:

def deepMembers[A: scala.reflect.runtime.universe.TypeTag](a: A) = {
  import scala.reflect.runtime.universe._

  def members(s: Symbol): Map[String, String] =
    s.typeSignature.declarations.collect {
      case m: ModuleSymbol => members(m)
      case m: MethodSymbol if m.isAccessor => m.returnType match {
        case ConstantType(Constant(s: String)) => Map(m.name.decoded -> s)
        case _ => Map.empty[String, String]
      }
    }.foldLeft(Map.empty[String, String])(_ ++ _)

  members(typeOf[A].termSymbol)
}

它是这样工作的:

scala> deepMembers(Settings) foreach println
(Name,username)
(Predicate,predicate)
(Query,query)
(Password,password)
(Protocol,protocol)
(Host,host)

如果由于某种原因您不能使用常量值定义,则需要调整MethodSymbol案例以使用实例镜像,但从子对象递归收集键值对的基本方法是相同的。

于 2013-10-01T16:29:23.943 回答