Scala进阶(1)—— 反射 object 和 class
Scala进阶(1)—— 反射 object 和 class
这篇文章写一点自己的理解:
由于 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 sayHello 2 (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
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
反射执行
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 sayHelloSymbol 2 = moduleMirror.symbol.typeSignature.member(universe.TermName( "sayHello2" )).asMethod // MethodMirror and invoke action objectMirror.reflectMethod(sayHelloSymbol)() objectMirror.reflectMethod(sayHelloSymbol 2 )( "Sai" , "Gerrard" ) // Pass the input parameters one by one } |