我需要使用反射来获取在包对象上定义的字段。
package com.coryklein
package object kittens {
val purr = "meow"
}
mirror文档说:
Scala只有实例方法——对象的方法是对象实例的实例方法,可通过ModuleMirror.instance
获得
甚至有一个很好的例子,使用反射在一个已定义的类C
上调用方法x
。
scala> class C { def x = 2 }
defined class C
scala> val im = m.reflect(new C)
im: reflect.runtime.universe.InstanceMirror = instance mirror for C@3442299e
scala> val methodX = typeOf[C].declaration(TermName("x")).asMethod
methodX: reflect.runtime.universe.MethodSymbol = method x
scala> val mm = im.reflectMethod(methodX)
mm: reflect.runtime.universe.MethodMirror = method mirror for C.x: scala.Int (bound to C@3442299e)
scala> mm()
res0: Any = 2
从这里看来,适应这种方法来反映存在于包对象上的字段应该不是太困难。但是,包对象没有相应的类来通过反射获取该字段。
我已经能够获得术语符号:
val ts: TermSymbol = mirror.staticPackage("com.coryklein.kittens").info.decls
.find(_.name.decodedName.toString == "purr").get.asTerm
但是在这一点上,我没有一个实例镜像等价于上面的im
来反射这个字段。
我如何通过Scala反射访问在包对象kittens
中定义的purr
val ?
这里的关键部分是找到实际的模块,它最终被命名为com.coryklein.kittens.package$
:
scala> import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.{universe=>ru}
scala> val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
runtimeMirror: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@67ee3ed7 of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [(memory)] and parent being scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@53b79b76 of type class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader with classpath [file:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/resources.jar,file:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/rt.jar,file:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jsse.jar,file:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jce.jar,file:/Library/Jav...
scala> val moduleSymbol = runtimeMirror.staticModule("com.coryklein.kittens.package$")
moduleSymbol: reflect.runtime.universe.ModuleSymbol = object package$
scala> val moduleMirror = runtimeMirror.reflectModule(moduleSymbol)
moduleMirror: reflect.runtime.universe.ModuleMirror = module mirror for com.coryklein.kittens.package$ (bound to null)
scala> val moduleInstance = moduleMirror.instance
moduleInstance: Any = com.coryklein.kittens.package$@72d3f96
scala> val instanceMirror = runtimeMirror.reflect(moduleInstance)
instanceMirror: reflect.runtime.universe.InstanceMirror = instance mirror for com.coryklein.kittens.package$@72d3f96
scala> val fieldSymbol = instanceMirror.symbol.info.member(ru.TermName("purr")).asTerm.accessed.asTerm
fieldSymbol: reflect.runtime.universe.TermSymbol = value purr
scala> val fieldMirror = instanceMirror.reflectField(fieldSymbol)
fieldMirror: reflect.runtime.universe.FieldMirror = field mirror for private[this] val purr: java.lang.String (bound to com.coryklein.kittens.package$@72d3f96)
scala> fieldMirror.get
res0: Any = meow
scala>
我通过在我的target/scala-2.12/classes
目录中戳出模块的名称,并在这里和那里运行javap
。