Scala反射之Type Symbol与Mirror的关系
The relationship between Type Symbol and Mirror of Scala reflection
Scala 反射真的很复杂。它包含类型符号和镜像。你能告诉我他们之间的关系吗?
在使用 Scala 反射 API 时,如果您使用 Java 反射 API,您会遇到更多类型。给定一个示例场景,您从 String
开始,其中包含 class 的完全限定 class 名称,这些是您可能遇到的类型:
Universe:Scala同时支持运行时和编译时反射。您可以通过从相应的宇宙中导入来选择要进行的反射类型。对于运行时反射,这对应于 scala.reflect.runtime
包,对于编译时反射,它对应于 scala.reflect.macros
包。本回答侧重于前者。
与 Java 一样,您通常通过选择要反映的 ClassLoader 的 class 开始任何反思。 Scala 提供了使用当前 class 的 ClassLoader
的快捷方式:scala.reflect.runtime.currentMirror
,这为您提供了 Mirror
(稍后会详细介绍镜像)。许多 JVM 应用程序只使用一个 class 加载器,因此这是 Scala 反射 API 的常见入口点。由于您是从 runtime
导入的,所以您现在就在那个宇宙中。
Symbols:符号包含有关您要反映的任何内容的静态元数据。这包括你能想到的任何东西:这个东西是不是一个caseclass,是不是一个字段,是不是一个class,类型参数是什么,是不是抽象的等等。你可以不查询任何东西这可能取决于当前的词法范围,例如 class 有哪些成员。您也可能不会以任何方式与您反映的事物进行交互(例如访问字段或调用方法)。您可以只查询元数据。
词法作用域是您在进行反射时可以 "see" 的所有内容,不包括隐式作用域(请参阅 this SO 了解不同作用域的处理方式)。 class 的成员如何随词法范围变化?想象一个带有单个 def foo: String
的抽象 class。名称 foo
可能在一个上下文中绑定到 def
(如果你查询它,会给你一个 MethodSymbol
)或者它可能在另一个上下文中绑定到 val
(给你一个TermSymbol
)。使用符号时,通常必须明确说明您期望的符号类型,您可以通过方法 .asTerm
、.asMethod
、.asClass
等
来实现
继续我们开始的 String
示例。您使用 Mirror
导出 ClassSymbol
描述 class:currentMirror.staticClass(myString)
.
Types: Types lets you query information about the type the symbol refers to in the current lexical context. You typically use Type
s for two things: querying what vars, vals and defs there are, and querying type relationships (e.g. is this type a subclass of that type). There are two ways of getting hold of a Type
. Either through a TypeSymbol
(ClassSymbol
is a TypeSymbol
) or through a TypeTag
.
继续该示例,您将在您获得 Type
.
的符号上调用 .toType
方法
Scopes:当你向 Type
请求 .members
或 .decl
时——这是是什么给了你术语(vars 和 vals)和方法——你得到了当前词法范围内成员的 Symbol
s 列表。此列表保存在类型 MemberScope
中,它只是一个美化的 List[Symbol]
.
在我们上面带有摘要 class 的示例中,此列表将包含名称 foo
的 TermSymbol
或 MethodSymbol
,具体取决于当前范围。
- Names:名称有两种形式:
TermName
和 TypeName
。它只是 String
的包装器。您可以使用类型来确定任何 Name
. 所命名的内容
- Mirrors:最后,镜子是您用来与 "something" 交互的工具。您通常从
Symbol
开始,然后使用该符号为您要与之交互的方法、构造函数或字段派生符号。当您拥有所需的符号时,您可以使用 currentMirror
为这些符号创建镜像。 Mirrors 让您可以调用构造函数 (ClassMirror
)、访问字段 (FieldMirror
) 或调用方法 (MethodMirror
)。您不能使用镜像来查询有关被反射事物的元数据。
所以把一个例子放在一起反映上面的描述,这就是你如何搜索字段,调用构造函数和读取 val
,给定 String
和完全限定的 class 姓名:
// 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"
Scala 反射真的很复杂。它包含类型符号和镜像。你能告诉我他们之间的关系吗?
在使用 Scala 反射 API 时,如果您使用 Java 反射 API,您会遇到更多类型。给定一个示例场景,您从 String
开始,其中包含 class 的完全限定 class 名称,这些是您可能遇到的类型:
Universe:Scala同时支持运行时和编译时反射。您可以通过从相应的宇宙中导入来选择要进行的反射类型。对于运行时反射,这对应于
scala.reflect.runtime
包,对于编译时反射,它对应于scala.reflect.macros
包。本回答侧重于前者。与 Java 一样,您通常通过选择要反映的 ClassLoader 的 class 开始任何反思。 Scala 提供了使用当前 class 的
ClassLoader
的快捷方式:scala.reflect.runtime.currentMirror
,这为您提供了Mirror
(稍后会详细介绍镜像)。许多 JVM 应用程序只使用一个 class 加载器,因此这是 Scala 反射 API 的常见入口点。由于您是从runtime
导入的,所以您现在就在那个宇宙中。Symbols:符号包含有关您要反映的任何内容的静态元数据。这包括你能想到的任何东西:这个东西是不是一个caseclass,是不是一个字段,是不是一个class,类型参数是什么,是不是抽象的等等。你可以不查询任何东西这可能取决于当前的词法范围,例如 class 有哪些成员。您也可能不会以任何方式与您反映的事物进行交互(例如访问字段或调用方法)。您可以只查询元数据。
词法作用域是您在进行反射时可以 "see" 的所有内容,不包括隐式作用域(请参阅 this SO 了解不同作用域的处理方式)。 class 的成员如何随词法范围变化?想象一个带有单个
来实现def foo: String
的抽象 class。名称foo
可能在一个上下文中绑定到def
(如果你查询它,会给你一个MethodSymbol
)或者它可能在另一个上下文中绑定到val
(给你一个TermSymbol
)。使用符号时,通常必须明确说明您期望的符号类型,您可以通过方法.asTerm
、.asMethod
、.asClass
等继续我们开始的
String
示例。您使用Mirror
导出ClassSymbol
描述 class:currentMirror.staticClass(myString)
.Types: Types lets you query information about the type the symbol refers to in the current lexical context. You typically use
Type
s for two things: querying what vars, vals and defs there are, and querying type relationships (e.g. is this type a subclass of that type). There are two ways of getting hold of aType
. Either through aTypeSymbol
(ClassSymbol
is aTypeSymbol
) or through aTypeTag
.继续该示例,您将在您获得
Type
. 的符号上调用 Scopes:当你向
Type
请求.members
或.decl
时——这是是什么给了你术语(vars 和 vals)和方法——你得到了当前词法范围内成员的Symbol
s 列表。此列表保存在类型MemberScope
中,它只是一个美化的List[Symbol]
.在我们上面带有摘要 class 的示例中,此列表将包含名称
foo
的TermSymbol
或MethodSymbol
,具体取决于当前范围。- Names:名称有两种形式:
TermName
和TypeName
。它只是String
的包装器。您可以使用类型来确定任何Name
. 所命名的内容
- Mirrors:最后,镜子是您用来与 "something" 交互的工具。您通常从
Symbol
开始,然后使用该符号为您要与之交互的方法、构造函数或字段派生符号。当您拥有所需的符号时,您可以使用currentMirror
为这些符号创建镜像。 Mirrors 让您可以调用构造函数 (ClassMirror
)、访问字段 (FieldMirror
) 或调用方法 (MethodMirror
)。您不能使用镜像来查询有关被反射事物的元数据。
.toType
方法
所以把一个例子放在一起反映上面的描述,这就是你如何搜索字段,调用构造函数和读取 val
,给定 String
和完全限定的 class 姓名:
// 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"