2016-03-29 12 views
2

私は非常にまれな使い方をしていますが、サードパーティが独自に実装している(プラグインアーキテクチャを考えています)、各特性のコンパニオンオブジェクトのフィールドを取得したい。 - 私は、各プラグインのIDを取得したいのですが、プラグインレジストリでTypeTag [T]からコンパニオンオブジェクトのフィールド値を取得

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

object HelloWorldPlugin { 
    val ID = "hello world" 
} 

シンプルな形質の実装は次のようになります。私たちは型の消去を扱っているので、Reflectionは私の唯一の選択肢のようです。 私は無駄に次のことを試してみました:

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]は常にクラスタイプとないモジュールタイプを返すことである - ので、私は、次のランタイムエラーを取得:

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にする最も良い方法は?

答えて

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

それぞれのオブジェクトの 'TypeTag [T]'にあなたが示唆しているものを理解しているかどうかは分かりません。これは私たちがこのスニペットでやっている最初のことではありませんか?あなたの提案のスニペットを共有できますか? – yarinbenado

関連する問題