类型符号与Scala反射镜的关系

ryoqjall  于 2023-06-23  发布在  Scala
关注(0)|答案(1)|浏览(129)

bounty还有7天到期。此问题的答案有资格获得+50声望奖励。The Unfun Cat想要奖励现有答案

Scala反射真的很复杂。它包含类型符号和镜像。你能告诉我他们之间的关系吗?

9o685dep

9o685dep1#

在使用Scala反射API时,您会遇到比使用Java反射API时更多的类型。举一个例子,如果你从一个包含一个类的完全限定类名的String开始,你可能会遇到以下类型:

  • Universe:Scala支持运行时和编译时反射。你可以通过从相应的宇宙中导入来选择你要做的反射类型。对于运行时反射,它对应于scala.reflect.runtime包,对于编译时反射,它对应于scala.reflect.macros包。本文的回答集中在前者。

像Java一样,您通常通过选择要反射的ClassLoader类来启动任何反射。Scala提供了一个使用当前类的ClassLoader的快捷方式:scala.reflect.runtime.currentMirror,这会给你一个Mirror(稍后会有更多关于镜像的内容)。许多JVM应用程序只使用一个类加载器,因此这是Scala反射API的一个公共入口点。因为你是从runtime导入的,所以你现在就在那个宇宙中。

  • Symbols:Symbols包含有关您想要反映的任何内容的静态元数据。这包括你能想到的任何东西:这是一个case类吗,是一个字段吗,是一个类吗,类型参数是什么,是抽象的吗,等等您不能查询任何可能依赖于当前词法范围的内容,例如类具有哪些成员。您也可以不以任何方式与您所反映的事物进行交互(例如访问字段或调用方法)。您可以只查询元数据。

词法作用域是您在执行反射的地方可以“看到”的所有内容,不包括隐式作用域(有关不同作用域的处理,请参阅this SO)。一个类的成员如何随词法作用域而变化?假设一个抽象类只有一个def foo: String。名称foo可能在一个上下文中绑定到def(如果您查询它,则会给您一个MethodSymbol),或者它可以在另一个上下文中绑定到val(给您一个TermSymbol)。当使用Symbols时,通常需要明确说明您期望的符号类型,您可以通过.asTerm.asMethod.asClass等方法来实现这一点。
继续我们开始的String示例。使用Mirror派生一个描述类的ClassSymbolcurrentMirror.staticClass(myString) .

  • Types:Types允许您查询有关符号在当前词法上下文中引用的类型的信息。Type s通常用于两件事:查询有什么var、val和def,以及查询类型关系(例如,此类型是否为该类型的子类)。获取Type的方法有两种。通过TypeSymbolClassSymbolTypeSymbol)或通过TypeTag

继续这个例子,你可以对你得到的符号调用.toType方法来得到Type

  • Scopes:当你向Type询问.members.decl时-这是给你的术语(var和vals)和方法-你得到一个当前词法作用域中成员的Symbol列表。这个列表保存在MemberScope类型中,它只是一个美化的List[Symbol]

在上面的抽象类示例中,根据当前作用域,该列表将包含名称为fooTermSymbolMethodSymbol

    • 名称 *:名称有两种:TermNameTypeName。它只是一个String的 Package 器。您可以使用类型来确定任何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"

相关问题