【问题标题】:The relationship between Type Symbol and Mirror of Scala reflectionScala反射的Type Symbol与Mirror的关系
【发布时间】:2018-03-31 00:00:56
【问题描述】:

Scala 反射非常复杂。它包含类型符号和镜像。你能告诉我他们之间的关系吗?

【问题讨论】:

    标签: scala reflection types symbols mirror


    【解决方案1】:

    使用 Scala 反射 API 时,您会遇到比使用 Java 反射 API 时所习惯的类型更多的类型。假设您以 String 开头,其中包含类的完全限定类名,那么您可能会遇到以下类型:

    • Universe:Scala 支持运行时和编译时反射。您可以通过从相应的宇宙中导入来选择您正在执行的反射类型。对于运行时反射,这对应于scala.reflect.runtime 包,对于编译时反射,它对应于scala.reflect.macros 包。这个答案侧重于前者。

      与 Java 一样,您通常通过选择要反射的 ClassLoader 的类来启动任何反射。 Scala 提供了使用当前类的ClassLoader 的快捷方式:scala.reflect.runtime.currentMirror,这为您提供了Mirror(稍后会详细介绍镜像)。许多 JVM 应用程序只使用一个类加载器,因此这是 Scala 反射 API 的常见入口点。由于您是从 runtime 导入的,因此您现在位于该 Universe 中。

    • Symbols:符号包含有关您想要反映的任何内容的静态元数据。这包括你能想到的任何东西:这个东西是一个案例类,它是一个字段,它是一个类,类型参数是什么,它是抽象的,等等。你可能不会查询任何可能取决于当前词法范围的东西,例如一个类有哪些成员。您也可能不会以任何方式与您反映的事物进行交互(例如访问字段或调用方法)。您可以只查询元数据。

      词法范围是您在进行反射的地方可以“看到”的所有内容,不包括隐式范围(有关不同范围的处理,请参阅this SO)。类的成员如何随词法作用域变化?想象一个带有单个def foo: String 的抽象类。名称foo 可能在一个上下文中绑定到def(如果您查询它,给你一个MethodSymbol)或者它可以在另一个上下文中绑定到一个val(给你一个TermSymbol) .使用符号时,通常必须明确说明您期望什么样的符号,您可以通过 .asTerm.asMethod.asClass 等方法来做到这一点。

      继续我们开始的String 示例。您使用Mirror 派生一个描述类的ClassSymbolcurrentMirror.staticClass(myString)

    • Types:Types 让您可以查询有关符号在当前词汇上下文中所指类型的信息。您通常将Types 用于两件事:查询有哪些 vars、vals 和 defs,以及查询类型关系(例如,此类型是否是该类型的子类)。有两种方法可以获取Type。通过TypeSymbolClassSymbolTypeSymbol)或通过TypeTag

      继续该示例,您将对获得Type 的符号调用.toType 方法。

    • Scopes:当你向Type 询问.members.decl——这就是给你术语(vars 和 vals)和方法的东西——你会得到一个@ 列表当前词法范围内的成员的 987654360@s。这个列表保存在MemberScope 类型中,它只是一个美化的List[Symbol]

      在我们上面的抽象类示例中,此列表将包含 TermSymbolMethodSymbol 名称 foo,具体取决于当前范围。

    • Names:名称有两种形式:TermNameTypeName。它只是String 的包装。您可以使用类型来确定任何Name 命名的内容。
    • Mirrors:最后,镜子是你用来与“某物”交互的东西。您通常从 Symbol 开始,然后使用该符号为要与之交互的方法、构造函数或字段派生符号。当您拥有所需的符号时,您可以使用currentMirror 为这些符号创建镜像。 Mirrors 允许您调用构造函数 (ClassMirror)、访问字段 (FieldMirror) 或调用方法 (MethodMirror)。您不得使用镜像来查询有关被反射事物的元数据。

    因此,将反映上述描述的示例放在一起,这就是搜索字段、调用构造函数并读取val 的方式,给定具有完全限定类名的String

    // Do runtime reflection on classes loaded by current ClassLoader
    val currentMirror: universe.Mirror = scala.reflect.runtime.currentMirror
    
    // Use symbols to navigate to pick out the methods and fields we want to invoke
    // Notice explicit symbol casting with the `.as*` methods.
    val classSymbol: universe.ClassSymbol = currentMirror.staticClass("com.example.Foo")
    val constructorSymbol: universe.MethodSymbol = classSymbol.primaryConstructor.asMethod
    val fooSymbol: Option[universe.TermSymbol] = classSymbol.toType.members.find(_.name.toString == "foo").map(_.asTerm)
    
    // Get mirrors for performing constructor and field invocations
    val classMirror: universe.ClassMirror = currentMirror.reflectClass(classSymbol)
    val fooInstance: Foo = classMirror.reflectConstructor(constructorSymbol).apply().asInstanceOf[Foo]
    val instanceMirror: universe.InstanceMirror = currentMirror.reflect(fooInstance)
    
    // Do the actual invocation
    val fooValue: String = instanceMirror.reflectField(fooSymbol.get).get.asInstanceOf[String]
    println(fooValue) // Prints the value of the val "foo" of the object "fooInstance"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 1970-01-01
      相关资源
      最近更新 更多