简体   繁体   English

从 TypeTag[T] 获取伴随对象的字段值

[英]Get field value of a companion object from TypeTag[T]

I have a quite rare use case where a trait is being implemented by 3rd party (think of a plugin architecture) and I want to get a field of each trait's companion object.我有一个非常罕见的用例,其中 trait 由 3rd 方实现(想想插件架构),我想获得每个 trait 的伴随对象的字段。

A simple trait implementation looks like this:一个简单的 trait 实现如下所示:

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

object HelloWorldPlugin {
  val ID = "hello world"
}

In the plugin registry - I'd like to get the ID of each plugin.在插件注册表中 - 我想获取每个插件的ID Since we're dealing with type erasure - Reflection seems like my only option.由于我们正在处理类型擦除 - 反射似乎是我唯一的选择。 I tried the following to no avail:我尝试了以下方法无济于事:

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"
  }
}

But the problem here is that typeOf[T] always returns the Class type and not the Module type - hence I get the following runtime error:但这里的问题是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)

What's the best way to get the ID value for each Plugin from its TypeTag ?TypeTag获取每个 Plugin 的ID值的最佳方法是什么?

It really depends on your use case, but I would highly advice against the approach you have taken to create your plugin design pattern.这实际上取决于您的用例,但我强烈建议您反对您创建插件设计模式所采用的方法。 The problem is that for each plugin, you have to define a class, a corresponding object, and finally to 'register' each plugin.问题是对于每个插件,你必须定义一个类,一个对应的对象,最后“注册”每个插件。 This creates a huge overhead and danger to create runtime errors.这会产生巨大的开销和产生运行时错误的危险。 Furthermore, the correspondence between the ID field in the companion object and its class is not enforced by the compiler, but its left to the mercy and the diligence of the developer.此外,伴生对象中的ID字段与其类之间的对应关系不是由编译器强制执行的,而是留给开发人员的怜悯和勤奋。

I believe that a more elegant approach would be to use the TypeTag[T] for each object that represents one of your plugins.我相信更优雅的方法是为代表您的插件之一的每个对象使用TypeTag[T] That way, you enforce the isomorphism between all implemented plugins, and all registered plugins, and avoid the creation of companion objects.这样,您可以在所有已实现的插件和所有已注册的插件之间强制执行同构,并避免创建伴随对象。

Nevertheless, even with the given constraints, you can extract the string value from the companion object.尽管如此,即使有给定的约束,您也可以从伴随对象中提取字符串值。 Using Scala 2.11.8, we have:使用 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"
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM