【问题标题】:Get field value of a companion object from TypeTag[T]从 TypeTag[T] 获取伴随对象的字段值
【发布时间】:2016-03-29 17:16:40
【问题描述】:

我有一个非常罕见的用例,其中一个 trait 由 3rd 方实现(想想插件架构),我想获取每个 trait 的伴随对象的字段。

一个简单的 trait 实现如下所示:

trait Plugin {
  val ID: String
}
class HelloWorldPlugin extends Plugin {
  val ID = HelloWorldPlugin.ID
}

object HelloWorldPlugin {
  val ID = "hello world"
}

在插件注册表中 - 我想获取每个插件的 ID。由于我们正在处理类型擦除 - 反射似乎是我唯一的选择。 我尝试了以下方法无济于事:

object CompanionReflectionDemo {
  import scala.reflect.runtime.{universe => ru}
  private lazy val universeMirror = ru.runtimeMirror(getClass.getClassLoader)

  def registerPlugin[T <: Plugin](implicit tt: ru.TypeTag[T])  = {
    val companionMirror = universeMirror.reflectModule(ru.typeOf[T].typeSymbol.companion.asModule)
    val m = universeMirror.reflect(companionMirror.instance)
    val field = m.reflectField(ru.typeOf[T].decl(ru.TermName("ID")).asTerm.accessed.asTerm)
    field.get
  }

  def main(args: Array[String]) {
    val x = registerPlugin[HelloWorldPlugin]
    println(x) // expecting "hello world"
  }
}

但这里的问题是 typeOf[T] 总是返回 Class 类型而不是 Module 类型 - 因此我收到以下运行时错误:

Exception in thread "main" scala.ScalaReflectionException: expected a member of object HelloWorldPlugin, you provided value org.reflect.HelloWorldPlugin.ID
    at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$abort(JavaMirrors.scala:115)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$ErrorNotMember(JavaMirrors.scala:121)
    at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$scala$reflect$runtime$JavaMirrors$JavaMirror$$checkMemberOf$1.apply(JavaMirrors.scala:214)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.ensuringNotFree(JavaMirrors.scala:204)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$checkMemberOf(JavaMirrors.scala:213)
    at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaInstanceMirror.reflectField(JavaMirrors.scala:236)
    at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaInstanceMirror.reflectField(JavaMirrors.scala:233)
    at org.reflect.CompanionReflectionDemo$.registerPlugin(Blah.scala:21)
    at org.reflect.CompanionReflectionDemo$.main(Blah.scala:26)

从每个插件的TypeTag 获取ID 值的最佳方法是什么?

【问题讨论】:

    标签: scala scala-2.11 scala-reflect


    【解决方案1】:

    这确实取决于您的用例,但我强烈建议您反对您创建插件设计模式所采用的方法。问题是对于每个插件,您必须定义一个类、一个相应的对象,最后要“注册”每个插件。这会产生巨大的开销和产生运行时错误的危险。此外,伴随对象中的ID字段与其类之间的对应关系不是由编译器强制执行的,而是由开发人员决定的。

    我相信更优雅的方法是对代表您的插件之一的每个对象使用TypeTag[T]。这样,您可以在所有已实现的插件和所有已注册的插件之间强制执行同构,并避免创建伴随对象。

    尽管如此,即使有给定的约束,您也可以从伴随对象中提取字符串值。使用 Scala 2.11.8,我们有:

    object CompanionReflectionDemo {
    
      import scala.reflect.runtime.{universe => ru}
      private lazy val rootMirror = ru.runtimeMirror(getClass.getClassLoader)
    
      def registerPlugin[T <: Plugin](implicit tt: ru.TypeTag[T]) : String = {
    
        val classMirror       = rootMirror.reflectClass(tt.tpe.typeSymbol.asClass)
        val companionSymbol   = classMirror.symbol.companion
        val companionInstance = rootMirror.reflectModule(companionSymbol.asModule)
        val companionMirror   = rootMirror.reflect(companionInstance.instance)
    
        val fieldSymbol = companionSymbol.typeSignature.decl(ru.TermName("ID")).asTerm
        val fieldMirror = companionMirror.reflectField(fieldSymbol)
    
        fieldMirror.get.asInstanceOf[String]
      }
    
      def main(args: Array[String]) {
        val x = registerPlugin[HelloWorldPlugin]
        println(x) // expecting "hello world"
      }
    }
    

    【讨论】:

    • 我不确定我是否理解您对每个对象的TypeTag[T] 的建议。这不是我们在这个 sn-p 中要做的第一件事吗?你能分享一下你的建议吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    相关资源
    最近更新 更多