【scala原理系列】Scala Mirrors原理类型用法示例详解
文章目录
一、由来原理
Scala Mirrors 是 Scala 编程语言的一个重要特性,它提供了在运行时反射和操作程序结构的能力。Mirrors 的原理基于 Scala 的反射机制,它允许开发者在运行时获取并操作类、对象、方法、字段等程序元素。
Scala 的 Mirrors 通过 scala.reflect
包来实现。这个包中的核心类是 Mirror
,它是一个用于反射操作的接口。Mirrors 可以分为两种类型:语言层面的 Mirrors 和虚拟机层面的 Mirrors。
语言层面的 Mirrors 主要用于对语言级别的信息进行反射操作,例如获取类的成员、调用方法等。它提供了一组高级抽象,如 ClassSymbol
、MethodSymbol
和 Type
等,用于表示类、方法和类型等元素。通过这些抽象,开发者可以在运行时获取类的结构信息,并对其进行操作。
虚拟机层面的 Mirrors 则提供了对底层虚拟机级别信息的反射操作。它主要通过 java.lang.reflect
包中的类进行操作,例如 Class
、Method
和 Field
等。通过虚拟机层面的 Mirrors,开发者可以获取更底层的信息,如访问私有成员、操作字节码等。
Scala Mirrors 的原理可以概括为以下几个步骤:
-
创建 Mirror 实例:通过调用
scala.reflect.runtime.universe.runtimeMirror
方法,可以创建一个 Mirror 实例,该实例用于对程序进行反射操作。 -
获取类的 Symbol:使用 Mirror 实例的
classSymbol
方法,可以获取给定类的符号(Symbol)。Symbol 是对类、对象、方法等程序元素的抽象,它包含了元素的名称、类型信息等。 -
操作类的成员:通过 Symbol 实例的方法,如
members
、methods
和fields
等,可以获取类的成员信息。开发者可以根据需要遍历成员列表,并对其进行操作,例如调用方法、设置字段值等。 -
调用方法和访问字段:通过 MethodSymbol 和 FieldSymbol 等 Symbol 的具体子类,可以获取方法和字段的详细信息,如参数类型、返回类型、修饰符等。通过这些信息,开发者可以动态地调用方法和访问字段的值。
总之,Scala Mirrors 基于 Scala 的反射机制,提供了在运行时获取和操作程序结构的能力。它通过 Symbol 和虚拟机级别的反射机制,使开发者可以在运行时动态地操作类、对象、方法和字段等元素,从而实现更灵活和动态的编程。
二、Environment、Universes和Mirrors
1. Environment
反射environment根据反射任务是在运行时还是编译时而有所不同。运行时或编译时使用的environment的区别被封装在所谓的universes(universe)中。
反射environment的另一个重要方面是我们可以通过所谓的Mirrors(mirror)访问的实体集合。
例如,通过运行时反射可访问的实体是由ClassloaderMirror提供的。该Mirrors只提供了对由特定类加载器加载的实体(包、类型和成员)的访问。
Mirrors不仅确定了可以通过反射访问的实体集合,还提供了对这些实体执行的反射操作。例如,在运行时反射中,可以使用调用者Mirrors(invoker mirror)来调用类的方法或构造函数。
2.Universes
存在两种主要类型的universes - 因为既存在运行时反射能力,也存在编译时反射能力,因此必须使用与当前任务相对应的universes。可以选择以下两个universes:
- scala.reflect.runtime.universe:用于运行时反射。
- scala.reflect.macros.Universe:用于编译时反射。
universes提供了一个接口,用于表示反射中使用的所有主要概念,例如类型(Types)、树(Trees)和注解(Annotations)。
3. Mirrors
所有通过反射提供的信息都是通过Mirrors来访问的。根据要获取的信息类型或要执行的反射操作,必须使用不同类型的Mirrors。类加载器Mirrors可用于获取类型和成员的表示。从类加载器Mirrors中,可以获取更专门的调用者Mirrors(最常用的Mirrors),它们实现了反射调用,如方法或构造函数调用以及字段访问。
总结:
- “Classloader”Mirrors:这些Mirrors通过staticClass/staticModule/staticPackage方法将名称转换为符号。
- “Invoker”Mirrors:这些Mirrors实现了反射调用,如MethodMirror.apply、FieldMirror.get等。这些“调用者”Mirrors是最常用的Mirrors类型。
3.1 运行时Mirrors
在运行时使用镜像的入口点是通过ru.runtimeMirror(),其中ru是scala.reflect.runtime.universe。
scala.reflect.api.JavaMirrors#runtimeMirror调用的结果是一个类加载器镜像,类型为scala.reflect.api.Mirrors#ReflectiveMirror,可以按名称加载符号。
类加载器镜像可以创建调用者镜像(包括scala.reflect.api.Mirrors#InstanceMirror、scala.reflect.api.Mirrors#MethodMirror、scala.reflect.api.Mirrors#FieldMirror、scala.reflect.api.Mirrors#ClassMirror和scala.reflect.api.Mirrors#ModuleMirror)。
以下是这两种类型的镜像交互的示例。
3.2 镜像的类型、用例及示例
3.2.1 ReflectiveMirror 用于按名称加载符号,并作为进入点到调用者镜像。
入口点:
val m = ru.runtimeMirror(<classloader>)。
示例:
scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = ...
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: scala.reflect.runtime.universe.Mirror = JavaMirror ...
3.2.2 InstanceMirror 用于创建用于方法和字段以及内部类和内部对象(模块)的调用者镜像。
入口点:
val im = m.reflect(<value>)。
示例:
scala> class C { def x = 2 }
defined class C
scala> val im = m.reflect(new C)
im: scala.reflect.runtime.universe.InstanceMirror = instance mirror for C@3442299e
3.2.3 MethodMirror用于调用实例方法。
MethodMirror
用于调用实例方法(Scala 只有实例方法 - 对象的方法是对象实例的实例方法,可通过 ModuleMirror.instance
获取)。
入口类
val mm = im.reflectMethod(<method symbol>)
以下示例展示了如何使用MethodMirror
调用一个返回整数的实例方法:
scala> val methodX = ru.typeOf[C].decl(ru.TermName("x")).asMethod
methodX: scala.reflect.runtime.universe.MethodSymbol = method x
scala> val mm = im.reflectMethod(methodX)
mm: scala.reflect.runtime.universe.MethodMirror = method mirror for C.x: scala.Int (bound to C@3442299e)
scala> mm()
res0: Any = 2
3.2.4 FieldMirror 用于获取/设置实例字段
(与方法一样,Scala只有实例字段,请参见上文)。
入口点:
val fm = im.reflectField(<field or accessor symbol>)。
示例:
scala> class C { val x = 2; var y = 3 }
defined class C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: scala.reflect.runtime.universe.Mirror = JavaMirror ...
scala> val im = m.reflect(new C)
im: scala.reflect.runtime.universe.InstanceMirror = instance mirror for C@5f0c8ac1
scala> val fieldX = ru.typeOf[C].decl(ru.TermName("x")).asTerm.accessed.asTerm
fieldX: scala.reflect.runtime.universe.TermSymbol = value x
scala> val fmX = im.reflectField(fieldX)
fmX: scala.reflect.runtime.universe.FieldMirror = field mirror for C.x (bound to C@5f0c8ac1)
scala> fmX.get
res0: Any = 2
scala> fmX.set(3)
scala> val fieldY = ru.typeOf[C].decl(ru.TermName("y")).asTerm.accessed.asTerm
fieldY: scala.reflect.runtime.universe.TermSymbol = variable y
scala> val fmY = im.reflectField(fieldY)
fmY: scala.reflect.runtime.universe.FieldMirror = field mirror for C.y (bound to C@5f0c8ac1)
scala> fmY.get
res1: Any = 3
scala> fmY.set(4)
scala> fmY.get
res2: Any = 4
3.2.5 ClassMirror 用于为构造函数创建调用者镜像。
入口点:对于静态类,
val cm1 = m.reflectClass(<class symbol>);
对于内部类,
val mm2 = im.reflectClass(<class symbol>)。
示例:
scala> case class C(x: Int)
defined class C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: scala.reflect.runtime.universe.Mirror = JavaMirror ...
scala> val classC = ru.typeOf[C].typeSymbol.asClass
classC: scala.reflect.runtime.universe.Symbol = class C
scala> val cm = m.reflectClass(classC)
cm: scala.reflect.runtime.universe.ClassMirror = class mirror for C (bound to null)
scala> val ctorC = ru.typeOf[C].decl(ru.termNames.CONSTRUCTOR).asMethod
ctorC: scala.reflect.runtime.universe.MethodSymbol = constructor C
scala> val ctorm = cm.reflectConstructor(ctorC)
ctorm: scala.reflect.runtime.universe.MethodMirror = constructor mirror for C.<init>(x: scala.Int): C (bound to null)
scala> ctorm(2)
res0: Any = C(2)
3.2.6 ModuleMirror 用于访问单例对象的实例。
入口点:对于静态对象,val mm1 = m.reflectModule();
对于内部对象,val mm2 = im.reflectModule()。示例:
scala> object C { def x = 2 }
defined module C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: scala.reflect.runtime.universe.Mirror = JavaMirror ...
scala> val objectC = ru.typeOf[C.type].termSymbol.asModule
objectC: scala.reflect.runtime.universe.ModuleSymbol = object C
scala> val mm = m.reflectModule(objectC)
mm: scala.reflect.runtime.universe.ModuleMirror = module mirror for C (bound to null)
scala> val obj = mm.instance
obj: Any = C$@1005ec04
3.3 编译时镜像
编译时镜像只使用类加载器镜像按名称加载符号。
类加载器镜像的入口点是通过scala.reflect.macros.Context#mirror实现的。
常用的方法包括scala.reflect.api.Mirror#staticClass、scala.reflect.api.Mirror#staticModule和scala.reflect.api.Mirror#staticPackage。例如:
import scala.reflect.macros.Context
case class Location(filename: String, line: Int, column: Int)
object Macros {
def currentLocation: Location = macro impl
def impl(c: Context): c.Expr[Location] = {
import c.universe._
val pos = c.macroApplication.pos
val clsLocation = c.mirror.staticModule("Location") // get symbol of "Location" object
c.Expr(Apply(Ident(clsLocation), List(Literal(Constant(pos.source.path)), Literal(Constant(pos.line)), Literal(Constant(pos.column)))))
}
}
值得注意的是,有几种高级替代方法可以避免手动查找符号。例如,typeOf[Location.type].termSymbol(或typeOf[Location].typeSymbol,如果我们需要一个ClassSymbol),这些方法是类型安全的,因为我们不必使用字符串来查找符号。
三. 多种用法及示例
以下是 Mirrors
的几种常见用法及相应的代码示例:
1 创建 Mirror 实例
import scala.reflect.runtime.universe._
val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
2 获取类的运行时Mirrors
import scala.reflect.runtime.universe._
val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
val classSymbol: ClassSymbol = typeOf[MyClass].typeSymbol.asClass
val classMirror: ClassMirror = mirror.reflectClass(classSymbol)
3 创建对象的运行时Mirrors
import scala.reflect.runtime.universe._
val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
val classSymbol: ClassSymbol = typeOf[MyClass].typeSymbol.asClass
val classMirror: ClassMirror = mirror.reflectClass(classSymbol)
val constructor: MethodSymbol = typeOf[MyClass].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror: MethodMirror = classMirror.reflectConstructor(constructor)
val instance: MyClass = constructorMirror().asInstanceOf[MyClass]
4 访问对象的成员
import scala.reflect.runtime.universe._
val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
val instance: MyClass = // 获取 MyClass 的实例
val instanceMirror: InstanceMirror = mirror.reflect(instance)
val field: TermSymbol = typeOf[MyClass].decl(TermName("myField")).asTerm
val fieldMirror: FieldMirror = instanceMirror.reflectField(field)
val value: Any = fieldMirror.get