Scala 进阶(1)—— 反射 object 和 class

1. Scala 的 反射

关于 Scala 反射的具体内容,可以参考官方文档:https://docs.scala-lang.org/overviews/reflection/overview.html

这篇文章写一点自己的理解:

  • 由于 Scala 编译出来的内容是与 Java 相同的字节码文件,所以可以使用 Java 反射的相关方法来实现 Scala 代码的反射。
  • Scala 自己写了一套基于 Scala 的反射,具体的实现在 scala.reflect 这个 package 下面。
  • 这篇文章主要介绍,反射 Scala 中的 class 和 object 类的方法。

 

先看基础代码:

1
2
3
4
5
6
7
8
9
10
11
12
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 步骤完全一致,所以不赘述,直接贴代码:

1
2
3
4
5
6
7
8
9
10
11
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

3.1 使用 Scala 方式反射 Scala class

步骤如下:

  1. 通过 universe 和 classLoader 找到 JavaMirror
  2. 通过 JavaMirror 和 实例化对象 获取 InstanceMirror
  3. 通过 universe 获取目标类的 TypeTag
  4. 通过 TypeTag 获取目标类的 TermSymbol or MethodSymbol
  5. 获取 FieldMirror or MethodMirror
  6. 反射执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
}

 

posted @   Gerrard_Feng  阅读(751)  评论(0编辑  收藏  举报
编辑推荐:
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
阅读排行:
· DeepSeek智能编程
· 精选4款基于.NET开源、功能强大的通讯调试工具
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
点击右上角即可分享
微信分享提示