2016-03-29 10 views
2

Ich habe einen ziemlich seltenen Anwendungsfall, in dem ein Merkmal von Dritten implementiert wird (denke an eine Plugin-Architektur) und ich möchte ein Feld des Begleitobjekts jedes Merkmals erhalten .Erhalte Feldwert eines Begleitobjekts von TypeTag [T]

Eine einfache Merkmal Implementierung sieht wie folgt aus:

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

object HelloWorldPlugin { 
    val ID = "hello world" 
} 

In der Plugin-Registry - Ich möchte die ID jedes Plugin bekommen. Da es sich um eine Art Löschung handelt, scheint Reflection meine einzige Option zu sein. Ich habe versucht, die folgenden ohne Erfolg:

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

Aber das Problem dabei ist, dass typeOf[T] den Klassentyp immer wieder und nicht die Art Module - damit ich die folgenden Laufzeitfehler erhalten:

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) 

Was ist der beste Weg, um die ID Wert für jedes Plugin von seiner TypeTag zu bekommen?

Antwort

1

Es hängt wirklich von Ihrem Anwendungsfall ab, aber ich würde sehr von der Vorgehensweise abraten, die Sie zur Erstellung Ihres Plug-in-Entwurfsmusters gewählt haben. Das Problem ist, dass Sie für jedes Plugin eine Klasse, ein entsprechendes Objekt definieren und schließlich jedes Plugin "registrieren" müssen. Dies erzeugt einen großen Aufwand und die Gefahr, Laufzeitfehler zu erzeugen. Darüber hinaus wird die Entsprechung zwischen dem Feld ID in dem Companion-Objekt und seiner Klasse nicht vom Compiler durchgesetzt, aber es ist der Gnade und der Sorgfalt des Entwicklers überlassen.

Ich glaube, dass ein eleganterer Ansatz wäre, die TypeTag[T] für jedes Objekt zu verwenden, das eines Ihrer Plugins darstellt. Auf diese Weise erzwingen Sie die Isomorphie zwischen allen implementierten Plugins und allen registrierten Plugins und vermeiden die Erstellung von Companion-Objekten.

Nichtsdestoweniger, selbst mit den gegebenen Einschränkungen, können Sie den String-Wert aus dem Companion-Objekt extrahieren. Mit Scala 2.11.8 haben wir:

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

Ich bin mir nicht sicher, ich verstehe, was Sie vorschlagen mit Bezug auf die 'TypeTag [T]' für jedes Objekt. Ist das nicht das erste, was wir in diesem Ausschnitt machen? Könnten Sie einen Ausschnitt Ihres Vorschlags teilen? – yarinbenado