zoukankan      html  css  js  c++  java
  • 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 类的方法。

    先看基础代码:

    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

    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. 反射执行
      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
      }

  • 相关阅读:
    今天在国外网站上做了个测试,结果竟然这样
    中考总算考完了
    [转]从磁芯大战谈汇编
    位运算Pascal相关函数/过程
    本Blog已经迁往Wordpress.com
    PhyTopic测试结果...
    POJ1273 dinic Pascal模板
    C#解析xml文件
    并行和并发
    RI工具库参考
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/14695057.html
Copyright © 2011-2022 走看看