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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类型符号与Scala反射镜像之间的关系相关的知识,希望对你有一定的参考价值。

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

答案

使用Scala Reflection API时,如果使用Java Reflection API,则会遇到更多类型的内容。给出一个示例场景,您可以从包含类的完全限定类名的String开始,这些是您可能遇到的类型:

  • Universe:Scala支持运行时和编译时反射。您可以通过从相应的Universe导入来选择正在执行的反射。对于运行时反射,这对应于scala.reflect.runtime包,对于编译时反射它对应于scala.reflect.macros包。这个答案集中在前者身上。 与Java一样,您通常可以通过选择要反映的ClassLoader类来开始任何反射。 Scala提供了使用当前类的ClassLoader的快捷方式:scala.reflect.runtime.currentMirror,这将为您提供Mirror(稍后将更多关于镜像)。许多JVM应用程序只使用一个类加载器,因此这是Scala Reflection API的常见入口点。既然你是从runtime进口的,那么你现在就在那个宇宙中。
  • Symbols:符号包含有关您想要反映的内容的静态元数据。这包括你能想到的任何东西:这是一个案例类,它是一个字段,它是一个类,什么是类型参数,它是抽象的等等你可能不会查询任何可能取决于当前词法范围的东西,例如一个班级的成员。您也可能无法以任何方式与您反映的事物进行交互(例如访问字段或调用方法)。您只需查询元数据即可。 一个班级的成员如何根据词汇范围而变化?想象一个带有单个def foo: String的抽象类。名称foo可能在一个上下文中绑定到def(如果您查询它,可以给你一个MethodSymbol),或者它可以在另一个上下文中绑定到val(给你一个TermSymbol)。使用符号时,通常需要明确说明您期望的符号类型,通过.asTerm.asMethod.asClass等方法来实现。 继续我们开始的String示例。您使用Mirror派生出描述该类的ClassSymbolcurrentMirror.staticClass(myString)
  • Types:Types允许您查询有关当前词汇上下文中符号引用的类型的信息。您通常使用Types来做两件事:查询有哪些vars,val和defs,以及查询类型关系(例如,此类型是该类型的子类)。有两种方法可以获得Type。通过TypeSymbolClassSymbolTypeSymbol)或通过TypeTag。 继续这个例子,你将在你获得.toType的符号上调用Type方法。
  • Scopes:当你向Type询问.members.decl时 - 这就是给你术语(vars和vals)和方法 - 你得到当前词汇范围内成员的Symbols列表。这个列表保存在MemberScope类型中,它只是一个美化的List[Symbol]。 在我们上面的抽象类的示例中,根据当前范围,此列表将包含名称为TermSymbolMethodSymbolfoo
  • Names:名字有两种:TermNameTypeName。它只是String的包装。您可以使用该类型来确定任何Name命名的内容。
  • Mirrors:最后镜子是你用来与“某事”互动的东西。您通常从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"

以上是关于类型符号与Scala反射镜像之间的关系的主要内容,如果未能解决你的问题,请参考以下文章

通过 Java 反射找到 Scala 类型的伴生对象的可靠方法是啥?

通过 Java 反射找到 Scala 类型的伴生对象的可靠方法是啥?

Scala类型声明与定义函数定义流程控制异常处理

2021年大数据常用语言Scala:基础语法学习 数据类型与操作符

Scala之旅-变性

Scala系统学习:Scala数据类型