【scala原理系列】Scala Mirrors原理类型用法示例详解

本文详细解释了Scala中的ScalaMirrors原理,包括其环境、universes和不同类型的镜子(如ReflectiveMirror、InstanceMirror等)。文章介绍了如何在运行时和编译时创建镜像,操作类、对象、方法和字段,并提供了实际的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【scala原理系列】Scala Mirrors原理类型用法示例详解

一、由来原理

Scala Mirrors 是 Scala 编程语言的一个重要特性,它提供了在运行时反射和操作程序结构的能力。Mirrors 的原理基于 Scala 的反射机制,它允许开发者在运行时获取并操作类、对象、方法、字段等程序元素。

Scala 的 Mirrors 通过 scala.reflect 包来实现。这个包中的核心类是 Mirror,它是一个用于反射操作的接口。Mirrors 可以分为两种类型:语言层面的 Mirrors 和虚拟机层面的 Mirrors。

语言层面的 Mirrors 主要用于对语言级别的信息进行反射操作,例如获取类的成员、调用方法等。它提供了一组高级抽象,如 ClassSymbolMethodSymbolType 等,用于表示类、方法和类型等元素。通过这些抽象,开发者可以在运行时获取类的结构信息,并对其进行操作。

虚拟机层面的 Mirrors 则提供了对底层虚拟机级别信息的反射操作。它主要通过 java.lang.reflect 包中的类进行操作,例如 ClassMethodField 等。通过虚拟机层面的 Mirrors,开发者可以获取更底层的信息,如访问私有成员、操作字节码等。

Scala Mirrors 的原理可以概括为以下几个步骤:

  1. 创建 Mirror 实例:通过调用 scala.reflect.runtime.universe.runtimeMirror 方法,可以创建一个 Mirror 实例,该实例用于对程序进行反射操作。

  2. 获取类的 Symbol:使用 Mirror 实例的 classSymbol 方法,可以获取给定类的符号(Symbol)。Symbol 是对类、对象、方法等程序元素的抽象,它包含了元素的名称、类型信息等。

  3. 操作类的成员:通过 Symbol 实例的方法,如 membersmethodsfields 等,可以获取类的成员信息。开发者可以根据需要遍历成员列表,并对其进行操作,例如调用方法、设置字段值等。

  4. 调用方法和访问字段:通过 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

四、 参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值