2016-08-05 43 views
3

Ich habe zwei Züge und ihre Instanzobjekte wie folgt definiert lösen generische Eigenschaft von Namen zur Laufzeit. Forexample, wenn ich "min" sagen, ich will MinMonoid Objekt, "avg"AvgMonoid Objekt geben sollte, etc. So habe ich Setup folgende:Scala Reflexion

object Test extends App { 

    val AGGREGATORS_NAME_DICT = Map(
    "avg" -> "reflection.AvgMonoid", 
    "min" -> "reflection.MinMonoid", 
    "max" -> "reflection.MaxMonoid" 
) 
    val AGGREGATORS_ADT_DICT = Map(
    "avg" -> "reflection.Avg", 
    "min" -> "scala.Double", 
    "max" -> "scala.Double" 
) 

    val mirror = runtimeMirror(getClass.getClassLoader) 

    def stringToTypeTag[A](name: String): TypeTag[A] = { 
    val tpe = mirror.staticClass(name).selfType 
    TypeTag(mirror, new api.TypeCreator { 
     def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) = 
     if (m eq mirror) tpe.asInstanceOf[U#Type] 
     else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.") 
    }) 
    } 

    def resolve[T](fname: String): Option[Monoid[T] with ADTHelper[T]] = for { 
    monoidName <- AGGREGATORS_NAME_DICT.get(fname) 
    adtName <- AGGREGATORS_ADT_DICT.get(fname) 
    tag <- Option{stringToTypeTag[T](adtName)} 
    instance <- Option { 
     mirror 
     .reflectModule(mirror.staticModule(monoidName)) 
     .instance 
     .asInstanceOf[Monoid[T] with ADTHelper[T]] 
    } 
    } yield instance 
} 

Das Problem ist jetzt: ich tun kann:

println(resolve("min").get.op(1.0, 2.0)) 

aber ich kann nicht tun:

val monoid = resolve("min").get 
println(monoid.op(1.0, 2.0)) 

, weil die Art der monoid in späteren ist Monoid[Nothing] with ADTHelper[Nothing]. Ich kann den zugrunde liegenden Typ T von trait Monoid[T] und trait ADTHelper[T] mit der von mir entwickelten Methode resolve nicht lösen. Wie kann ich die resolve Funktion so ändern, dass sie die Eigenschaften mit Basiswert T löst ???

Ich weiß, wenn ich mit resolve[Double](...) aufrufen, wird es funktionieren, aber ich möchte dies in Laufzeit von der AGGREGATORS_ADT_DICT auflösen.

Antwort

2

Auflösung T zur Laufzeit ist unmöglich, weil dort ist keine T zu lösen dann! Wenn Sie resolve("min") schreiben, hat der Compiler , um sein Typenargument während Kompilierung zu bestimmen. Und weil die Argumente nichts über T sagen, und der Kontext keinen erwarteten Typ liefert (zB val monoid: Monoid[Double] = resolve("min"), wird er auf Nothing schließen. Und beachten Sie, dass der Code resolve hier überhaupt keine Rolle spielt, nur seine type signature

Man könnte resolve ein Makro machen, also T zur Laufzeit des Makros bestimmen, also die Kompilierzeit des Programms mit diesem Makro, aber das würde nur funktionieren, wenn man den Namen beim Kompilieren kennt (zB ist es eine wörtliche Zeichenkette)

+0

Danke für den Kommentar.So, es ist wegen Löschen in JVM, oder? Jede andere Möglichkeit, die Sie vorschlagen können, um dies zu erreichen? – bistaumanga

+0

Eigentlich ich w ant, um diese Karten aus der Konfigurationsdatei zu holen. Ich bin mir nicht sicher, ob die Verwendung von Makros funktionieren wird, weil ich möchte, dass diese Merkmale später (außerhalb dieser Codebasis) erweiterbar sind und dies nur die Maps in der Konfigurationsdatei bereitstellt und Objekte bereitstellt, die diese Eigenschaften erweitern. – bistaumanga

+1

Auch wenn JVM keine Typen löschte, mussten die Typargumente noch zur Kompilierzeit für die Typprüfung bestimmt werden. –