此文件頁面專門針對 Scala 2 中提供的功能,這些功能已在 Scala 3 中移除或由其他功能取代。除非另有說明,此頁面中的所有程式碼範例都假設您使用 Scala 2。
實驗性
環境
反射環境會根據反射工作是在執行階段還是編譯階段而有所不同。在執行階段或編譯階段使用的環境之間的區別封裝在所謂的宇宙中。反射環境的另一個重要面向是我們具有反射存取權的實體集合。此實體集合由所謂的鏡像決定。
例如,可透過執行時間反射存取的實體是由 ClassloaderMirror
提供的。此鏡像僅提供特定類別載入器載入的實體(套件、類型和成員)的存取權。
鏡像不僅決定可透過反射存取的實體組。它們還提供要在這些實體上執行的反射操作。例如,在執行時間反射中,呼叫鏡像可用於呼叫類別的方法或建構函式。
宇宙
有兩種主要的宇宙類型,由於同時存在執行時間和編譯時間反射功能,因此必須使用與手邊任務相應的宇宙。下列任一宇宙:
scala.reflect.runtime.universe
適用於執行時間反射,或scala.reflect.macros.Universe
適用於編譯時間反射。
宇宙提供介面存取反射中使用所有主要概念,例如 Types
、Trees
和 Annotations
。
鏡像
反射提供的所有資訊都是透過鏡像存取的。根據要取得的資訊類型或要執行的反射動作,必須使用不同類型的鏡像。類別載入器鏡像可用於取得類型和成員的表示。從類別載入器鏡像,可以取得更專業的呼叫鏡像(最常用的鏡像),實作反射呼叫,例如方法或建構函式呼叫和欄位存取。
摘要
-
「類別載入器」鏡像。這些鏡像將名稱轉換為符號(透過方法
staticClass
/staticModule
/staticPackage
)。 -
「呼叫」鏡像。這些鏡像實作反射呼叫(透過方法
MethodMirror.apply
、FieldMirror.get
等)。這些「呼叫」鏡像是最常用的鏡像類型。
執行時間鏡像
在執行階段使用鏡像的進入點是透過 ru.runtimeMirror(<classloader>)
,其中 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
)。
以下提供這兩種鏡像互動方式的範例。
鏡像類型、其使用案例和範例
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
編譯時期鏡像
編譯時期鏡像僅使用類別載入器鏡像,以載入符號名稱。
進入類別載入器鏡像的進入點是透過 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
),因為我們不必使用字串來查詢符號,所以這些是類型安全的。