1. Scala 的 反射
關於 Scala 反射的具體內容,可以參考官方文檔:https://docs.scala-lang.org/overviews/reflection/overview.html
這篇文章寫一點自己的理解:
- 由於 Scala 編譯出來的內容是與 Java 相同的字節碼文件,所以可以使用 Java 反射的相關方法來實現 Scala 代碼的反射。
- Scala 自己寫了一套基於 Scala 的反射,具體的實現在 scala.reflect 這個 package 下面。
- 這篇文章主要介紹,反射 Scala 中的 class 和 object 類的方法。
先看基礎代碼:
package com.personal
object ProvisioningApp {
val strInObj = "123"
def sayHello(): Unit = println("say hello")
def sayHello2(from: String, to: String): Unit = println(from + " say hello, " + to)
}
class ProvisioningApp {
val strInClazz = "234"
def sayGoodbye(): Unit = println("say goodbye")
}
2. Java Style
2.1 使用 Java 的方式反射 Scala class
和反射 Java 的 class 步驟完全一致,所以不贅述,直接貼代碼:
test("Should reflect Scala class in Java style") {
val app = new ProvisioningApp
val field = classOf[ProvisioningApp].getDeclaredField("strInClazz")
field.setAccessible(true)
field.set(app, "789")
app.strInClazz shouldBe "789"
val method = classOf[ProvisioningApp].getDeclaredMethod("sayGoodbye")
method.setAccessible(true)
method.invoke(app)
}

2.2 使用 Java 的方式反射 Scala object
Scala 中的 object,稱之為 “伴生類”,想要反射獲取它的類或者方法,首先要知道它編譯之后是個什么東西:


通過 jd-gui,我們知道了 Scala object 的具體實現:
- 編譯之后的類名是 "類名+$" 形式
- 屬性的名字,有時會和在代碼中定義的不同(在這個例子里面沒有顯現,具體原因我還不知道,比如這個 "strInObj", 有時候這個類名會變成 “$com.$personal.$$strInObj” 這樣)
- 可以發現,這是一個使用靜態代碼塊模式的單例,詳見 https://www.cnblogs.com/jing-an-feng-shao/p/7501617.html
- 因此,暫時沒有找到使用 Java 方式反射 Scala object 的方法
3. Scala style
- Scala 反射的核心是 scala.reflect.runtime.universe
- 很多東西自己也沒有特別搞懂,內容參考了網上的博客:https://blog.csdn.net/feloxx/article/details/76034023
- 核心是通過 universe 對象,去依次獲得 “Mirror” 這個東西
3.1 使用 Scala 方式反射 Scala class
步驟如下:
- 通過 universe 和 classLoader 找到 JavaMirror
- 通過 JavaMirror 和 實例化對象 獲取 InstanceMirror
- 通過 universe 獲取目標類的 TypeTag
- 通過 TypeTag 獲取目標類的 TermSymbol or MethodSymbol
- 獲取 FieldMirror or MethodMirror
- 反射執行
test("Should reflect Scala class in Scala type") {
import scala.reflect.runtime.universe
val app = new ProvisioningApp()
// JavaMirror
val classMirror = universe.runtimeMirror(getClass.getClassLoader)
// InstanceMirror
val instanceMirror = classMirror.reflect(app)
// TypeTag
val typeTag = universe.typeOf[ProvisioningApp]
// TermSymbol
val strInClazzSymbol = typeTag.decl(universe.TermName("strInClazz")).asTerm
val fieldMirror = instanceMirror.reflectField(strInClazzSymbol)
fieldMirror.set("789")
app.strInClazz shouldBe "789"
// MethodSymbol
val sayGoodbyeSymbol = typeTag.decl(universe.TermName("sayGoodbye")).asMethod
// MethodMirror and invoke action
val result = instanceMirror.reflectMethod(sayGoodbyeSymbol)() // No input parameters here
result shouldBe "Goodbye"
}
3.2 使用 Scala 方式反射 Scala object
與 Scala class 不同,反射 Scala object 核心是通過 staticModule 獲取 ModuleMirror:
test("Should reflect Scala object in Scala style") {
import scala.reflect.runtime.universe
// JavaMirror
val classMirror = universe.runtimeMirror(getClass.getClassLoader)
// The ModuleSymbol for object
val staticMirror = classMirror.staticModule("com.personal.ProvisioningApp")
// ModuleMirror can be used to create instances of the class
val moduleMirror = classMirror.reflectModule(staticMirror)
// ObjectMirror can be used to reflect the members of the object
val objectMirror = classMirror.reflect(moduleMirror.instance)
// TermSymbol represents val, var, def and object declarations
val strInObjSymbol = moduleMirror.symbol.typeSignature.member(universe.TermName("strInObj")).asTerm
// FieldMirror can be used to get and set the value of the field
val fieldMirror = objectMirror.reflectField(strInObjSymbol)
fieldMirror.set("789")
ProvisioningApp.strInObj shouldBe "789"
val sayHelloSymbol = moduleMirror.symbol.typeSignature.member(universe.TermName("sayHello")).asMethod
val sayHelloSymbol2 = moduleMirror.symbol.typeSignature.member(universe.TermName("sayHello2")).asMethod
// MethodMirror and invoke action
objectMirror.reflectMethod(sayHelloSymbol)()
objectMirror.reflectMethod(sayHelloSymbol2)("Sai", "Gerrard") // Pass the input parameters one by one
}

