【Scala反射】Environment、Universe 和 Mirror

Environment、Universe 和 Mirror

Environment

反射環境(Environment)根據反射任務是在運行時仍是在編譯時而有所不一樣。在運行時或編譯時使用的環境之間的區別被封裝在一個所謂的universe中。 反射環境的另外一個重要方面是咱們能夠反射訪問的一組實體。 這組實體由所謂的mirror肯定。api

例如,經過運行時反射可訪問的實體可由 ClassloaderMirror 提供。此鏡像僅提供對由特定類加載器加載的實體(包,類型和成員)的訪問。安全

Mirror 不只肯定了可反射訪問的一組實體,他們還提供對這些實體進行反射的操做。例如,在運行時反射中,invoker mirror 可用於調用類的方法或構造函數。app

Universe

有兩種主要類型的 universe——由於存在運行時和編譯時反射能力,因此使用了與各自任務相對應的universe。有如下兩種:函數

  • scala.reflect.runtime.universe 用於運行時反射,或
  • scala.reflect.macros.Universe 用於編譯時反射。

universe 爲反射中使用的全部主要概念(如TypeTreeAnnotation)提供了一個接口。scala

Mirror

經過反射提供的全部信息均可以經過mirror訪問。 根據要獲取的信息類型或要採起的反射行爲,必須使用不一樣的mirror 風格。 Classloader mirror 可用於獲取類型和成員的表示。從類加載器鏡像中,能夠得到更專用的invoker mirror(最經常使用的鏡像),這些鏡像實現反射調用,例如方法或構造函數調用和字段訪問。code

概要:orm

  • 「Classloader」mirror。 這些鏡像將名稱轉換爲符號(經過 staticClass / staticModule / staticPackage方法)。
  • 「Invoker」mirror。 這些鏡像實現反射調用(經過方法MethodMirror.applyFieldMirror.get等)。 這些 invoker mirror 是最經常使用的鏡像類型。

運行時 Mirror

在運行時使用的 mirror 入口點是經過 ru.runtimeMirror(<classloader>),其中 ruscala.reflect.runtime.universe對象

scala.reflect.api.JavaMirrors#runtimeMirror 調用的結果是一個類加載器鏡像,類型爲scala.reflect.api.Mirrors#ReflectiveMirror,它能夠按名稱加載符號。接口

類加載器鏡像能夠建立調用者鏡像(包括 scala.reflect.api.Mirrors#InstanceMirrorscala.reflect.api.Mirrors#MethodMirrorscala.reflect.api.Mirrors#FieldMirrorscala.reflect.api.Mirrors#ClassMirrorscala.reflect.api.Mirrors#ModuleMirror)。ssl

這兩種類型的鏡像如何交互的例子能夠在下面找到。

Mirror 的類型,用例和例子

`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 ...

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

MethodMirror 用於調用實例方法(Scala只有實例方法 - 對象方法是對象實例的實例方法,可經過ModuleMirror.instance 獲取)。入口點:val mm = im.reflectMethod(<method symbol>)。 例:

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

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

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)

ModuleMirror 用於訪問單例對象的實例。入口點:對於靜態對象 val mm1 = m.reflectModule(<module symbol>),對於內部對象 val mm2 = im.reflectModule(<module symbol>)。例:

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

編譯時 Mirror

編譯時鏡像僅使用類加載器鏡像來按名稱加載符號。

類加載器鏡像的入口點是經過 scala.reflect.macros.Context#mirror。使用類加載器鏡像的典型方法包括scala.reflect.api.Mirror#staticClassscala.reflect.api.Mirror#staticModulescala.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)))))
  }
}

值得注意的是:有幾種高級別的選擇能夠用來避免手動查找符號。例如,Of[Location.type].termSymbol(或 typeOf[Location].typeSymbol,若是咱們須要一個 ClassSymbol),這是類型安全的,由於咱們沒必要使用字符串來查找符號。

相關文章
相關標籤/搜索