2016-07-03 13 views
5

Unter der Annahme, dass ich eine generische Oberklasse:In Scala Reflection, Wie generischer Typ Parameter einer konkreten Unterklasse erhalten?

class GenericExample[T](
         a: String, 
         b: T 
         ) { 

    def fn(i: T): T = b 
} 

und eine konkrete Unterklasse:

case class Example(
        a: String, 
        b: Int 
       ) extends GenericExample[Int](a, b) 

Ich mag die Typ-Parameter-Funktion „fn“ durch scala Reflexion erhalten, so dass ich wähle und Filter ihre Mitglieder durch:

import ScalaReflection.universe._ 

val baseType = typeTag[Example] 

val member = baseType 
    .tpe 
    .member(methodName: TermName) 
    .asTerm 
    .alternatives 
    .map(_.asMethod) 
    .head 

    val paramss = member.paramss 
    val actualTypess: List[List[Type]] = paramss.map { 
     params => 
     params.map { 
      param => 
      param.typeSignature 
     } 
    } 

ich scala erwartete mich das richtige Ergebnis zu erhalten, das List(List(Int)) ist, sondern ich nur g ot der Gattungs List(List(T))

durch das Dokument Knirschen ich, dass typeSignature gefunden ist der Täter:

* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an 
* instantiation of a generic type. 

Und es schlägt mir die Alternative zu verwenden:

def typeSignatureIn(site: Type): Type 

Da jedoch Klasse Beispiel nicht ist länger generisch, es gibt keine Möglichkeit, dass ich eine Site von typeTag [Beispiel] bekomme, kann mir jemand vorschlagen, wie typeOf [Int] nur typeTag [Example] erhalten wird? Oder gibt es keine Möglichkeit dies zu tun und ich muss auf Java-Reflektion zurückgreifen?

Vielen Dank für Ihre Hilfe.

UPDATE: Nach einigen schnellen Test fand ich, dass auch MethodSymbol.returnType nicht wie vorgesehen funktioniert, den folgenden Code:

member.returnType 

auch T ergeben, annd es kann nicht korrigiert werden von asSeenFrom, wie der folgende Code das Ergebnis nicht ändern:

member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass) 
+0

Noch bevor Sie tatsächlich Ihre Frage gelesen haben. Während meiner gesamten Erfahrung mit Scala besteht eines der Kernkonzepte darin, so viel wie möglich an den Compiler zu delegieren. Reflexion ist also nicht eines der Dinge, die du in Scala machen würdest. Sie können jedoch Makros oder Kontextgrenzen (Manifeste und ClassManifests) verwenden. – caeus

+0

Wird Manifest veraltet? Und ClassManifest in ClassTag umbenannt? Ich hoffe, ich kann den gelöschten ClassTag zumindest aus der Methode herausholen, aber es ist immer noch nicht möglich. – tribbloid

Antwort

0

ich meine Lösung bin Entsendung: I denke, es gibt keine Alternative aufgrund Scala Design:

Der Kernunterschied zwischen Methoden in Scala Reflexion & Java Reflexion ist Currying: Scala Methode compri In vielen Klammern wird beim Aufruf einer Methode mit Argumenten zunächst nur eine anonyme Klasse erstellt, die mehr Klammernpaare aufnehmen kann. Wenn keine Klammer mehr vorhanden ist, wird eine NullaryMethod-Klasse erstellt (a.k.a.). Call-by-Name), die aufgelöst werden können, um das Ergebnis der Methode zu liefern. So wird die Art der Scala-Methode nur auf dieser Ebene aufgelöst, wenn die Methode bereits in Methode & NullaryMethod Signatures unterteilt ist.

Als Ergebnis wird deutlich, dass der Ergebnistyp nur mit Rekursion erhalten werden kann:

private def methodSignatureToParameter_ReturnTypes(tpe: Type): (List[List[Type]], Type) = { 
    tpe match { 
     case n: NullaryMethodType => 
     Nil -> n.resultType 
     case m: MethodType => 
     val paramTypes: List[Type] = m.params.map(_.typeSignatureIn(tpe)) 
     val downstream = methodSignatureToParameter_ReturnTypes(m.resultType) 
     downstream.copy(_1 = List(paramTypes) ++ methodSignatureToParameter_ReturnTypes(m.resultType)._1) 
     case _ => 
     Nil -> tpe 
    } 
    } 

    def getParameter_ReturnTypes(symbol: MethodSymbol, impl: Type) = { 

    val signature = symbol.typeSignatureIn(impl) 
    val result = methodSignatureToParameter_ReturnTypes(signature) 
    result 
    } 

Wo impl ist die Klasse, die die Methode besitzt, und symbol ist, was man von Type.member(s) von scala Reflexion erhalten

+0

Ich hoffe, dass Methode in Scala und Java-Reflexion interoperabler gemacht werden kann, da sie beide ihren Vorteil haben: Java-Methode ist viel schneller zu starten, aber seine Laufzeittyp Check in JVM ist ziemlich locker, es gibt keine Perfektion hier: - < – tribbloid

4

Es gibt zwei ca. oaches die ich vorschlagen:

1) Reveal generischen Typ von Basisklasse:

import scala.reflect.runtime.universe._ 

class GenericExample[T: TypeTag](a: String, b: T) { 
    def fn(i: T) = "" + b + i 
} 

case class Example(a: String, b: Int) extends GenericExample[Int](a, b) {} 

val classType = typeOf[Example].typeSymbol.asClass 
val baseClassType = typeOf[GenericExample[_]].typeSymbol.asClass 
val baseType = internal.thisType(classType).baseType(baseClassType) 

baseType.typeArgs.head // returns reflect.runtime.universe.Type = scala.Int 

2) implizite Methode hinzufügen, den Typ zurückzugibt:

import scala.reflect.runtime.universe._ 

class GenericExample[T](a: String, b: T) { 
    def fn(i: T) = "" + b + i 
} 

case class Example(a: String, b: Int) extends GenericExample[Int](a, b) 

implicit class TypeDetector[T: TypeTag](related: GenericExample[T]) { 
    def getType(): Type = { 
    typeOf[T] 
    } 
} 

new Example("", 1).getType() // returns reflect.runtime.universe.Type = Int 
+0

Ich habe Angst in den meisten meiner Fälle GenericExample ist unbekannt und tief in einem willkürlichen Graph der Vererbung vergraben, so dass es nicht möglich ist, einen Companion Detector zu schreiben, aber ich werde versuchen, growing die erste Lösung, vielen Dank für Ihre Antwort! – tribbloid

+0

Funktioniert nicht: Fehler: (72, 20) nicht gefunden: Wert intern val baseType = internal.thisType (classType) .baseType (baseClassType). Ich benutze Scala 2.10.6 für die Kompatibilität mit einer Bibliothek – tribbloid

+0

Ich denke, ich habe jetzt eine Ahnung, MethodeSymbol von super generische Klasse immer Typ Parameter zu löschen, um es zu umgehen Ich muss stattdessen MethodType verwenden. – tribbloid