我有一个非常罕见的用例,第三方正在实现一个特征(想想一个插件架构),我希望获得每个特征的伴随对象的字段。
一个简单的特征实现如下:
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)
从ID
获取每个插件的TypeTag
值的最佳方法是什么?
答案 0 :(得分:1)
这实际上取决于您的用例,但我会高度反对您创建插件设计模式所采用的方法。问题是,对于每个插件,你必须定义一个类,一个相应的对象,最后注册&#39;每个插件。这会产生巨大的开销并且会产生运行时错误。此外,伴随对象中的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"
}
}