Scala反射非常复杂。它包含类型符号和镜像。你能告诉我他们之间的关系吗?
使用Scala Reflection API时,如果使用Java Reflection API,则会遇到更多类型的内容。给出一个示例场景,您可以从包含类的完全限定类名的String
开始,这些是您可能遇到的类型:
scala.reflect.runtime
包,对于编译时反射它对应于scala.reflect.macros
包。这个答案集中在前者身上。
与Java一样,您通常可以通过选择要反映的ClassLoader类来开始任何反射。 Scala提供了使用当前类的ClassLoader
的快捷方式:scala.reflect.runtime.currentMirror
,这将为您提供Mirror
(稍后将更多关于镜像)。许多JVM应用程序只使用一个类加载器,因此这是Scala Reflection API的常见入口点。既然你是从runtime
进口的,那么你现在就在那个宇宙中。def foo: String
的抽象类。名称foo
可能在一个上下文中绑定到def
(如果您查询它,可以给你一个MethodSymbol
),或者它可以在另一个上下文中绑定到val
(给你一个TermSymbol
)。使用符号时,通常需要明确说明您期望的符号类型,通过.asTerm
,.asMethod
,.asClass
等方法来实现。
继续我们开始的String
示例。您使用Mirror
派生出描述该类的ClassSymbol
:currentMirror.staticClass(myString)
。Type
s来做两件事:查询有哪些vars,val和defs,以及查询类型关系(例如,此类型是该类型的子类)。有两种方法可以获得Type
。通过TypeSymbol
(ClassSymbol
是TypeSymbol
)或通过TypeTag
。
继续这个例子,你将在你获得.toType
的符号上调用Type
方法。Type
询问.members
或.decl
时 - 这就是给你术语(vars和vals)和方法 - 你得到当前词汇范围内成员的Symbol
s列表。这个列表保存在MemberScope
类型中,它只是一个美化的List[Symbol]
。
在我们上面的抽象类的示例中,根据当前范围,此列表将包含名称为TermSymbol
的MethodSymbol
或foo
。TermName
和TypeName
。它只是String
的包装。您可以使用该类型来确定任何Name
命名的内容。Symbol
开始,然后使用该符号为您想要与之交互的方法,构造函数或字段派生符号。当您拥有所需的符号时,可以使用currentMirror
为这些符号创建镜像。镜像允许您调用构造函数(ClassMirror
),访问字段(FieldMirror
)或调用方法(MethodMirror
)。您可能无法使用镜像来查询有关所反映事物的元数据。所以举一个例子来反映上面的描述,这就是你如何搜索字段,调用构造函数和读取val
,给定一个具有完全限定类名的String
:
// Do runtime reflection on classes loaded by current ClassLoader
val currentMirror: universe.Mirror = scala.reflect.runtime.currentMirror
// Use symbols to navigate to pick out the methods and fields we want to invoke
// Notice explicit symbol casting with the `.as*` methods.
val classSymbol: universe.ClassSymbol = currentMirror.staticClass("com.example.Foo")
val constructorSymbol: universe.MethodSymbol = classSymbol.primaryConstructor.asMethod
val fooSymbol: Option[universe.TermSymbol] = classSymbol.toType.members.find(_.name.toString == "foo").map(_.asTerm)
// Get mirrors for performing constructor and field invocations
val classMirror: universe.ClassMirror = currentMirror.reflectClass(classSymbol)
val fooInstance: Foo = classMirror.reflectConstructor(constructorSymbol).apply().asInstanceOf[Foo]
val instanceMirror: universe.InstanceMirror = currentMirror.reflect(fooInstance)
// Do the actual invocation
val fooValue: String = instanceMirror.reflectField(fooSymbol.get).get.asInstanceOf[String]
println(fooValue) // Prints the value of the val "foo" of the object "fooInstance"