2016-07-03 27 views
5

Supponendo che ho una superclasse Generico:In Scala Reflection, Come ottenere un parametro di tipo generico di una sottoclasse di calcestruzzo?

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

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

e una sottoclasse concreta:

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

voglio ottenere il parametro di tipo di funzione "Fn" di Scala riflessione, in modo da selezionare e filtrare attraverso i suoi membri:

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

mi aspettavo scala di darmi il risultato corretto, che è List(List(Int)), invece solo io g ot il generico List(List(T))

Crunching attraverso il documento ho scoperto che typeSignature è il colpevole:

* 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. 

E mi suggerisce di utilizzare l'alternativa:

def typeSignatureIn(site: Type): Type 

Tuttavia, dal momento che di classe Esempio c'è più generico, non c'è modo di ottenere il sito da typeTag [Esempio], qualcuno può suggerirmi come ottenere typeOf [Int] dato solo typeTag [Example]? O non c'è modo di farlo e devo tornare alla riflessione Java?

Grazie mille per il vostro aiuto.

UPDATE: Dopo qualche prova pratica ho scoperto che anche MethodSymbol.returnType non funziona come previsto, il codice seguente:

member.returnType 

anche produrre T, annd non può essere corretto da asSeenFrom, come il seguente codice non cambia il risultato:

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

Prima ancora di aver letto la tua domanda. Durante tutta la mia esperienza con scala, uno dei suoi concetti base è di delegare il più possibile al compilatore. Quindi la riflessione non è una di quelle cose che dovresti fare in scala. Tuttavia è possibile utilizzare Macro o limiti di contesto (Manifests e ClassManifests) – caeus

+0

È Manifest in corso di deprecazione? E ClassManifest ridenominato in ClassTag? Spero di poter almeno ottenere il ClassTag cancellato dal metodo, ma ancora non è in grado di farlo. – tribbloid

risposta

0

sto postando la mia soluzione: I non ci sono alternative a causa del design di Scala:

La differenza principale tra i metodi nella riflessione di Scala & La riflessione di Java è in fase di elaborazione: Metodo Scala compri ses di molte coppie di parentesi, chiamare un metodo con argomenti prima costruisce semplicemente una classe anonima che può richiedere più coppie di parentesi, o se non c'è più parentesi a sinistra, costruisce una classe NullaryMethod (a.k.a. call-by-name) che può essere risolto per fornire il risultato del metodo. Pertanto, i tipi di metodo scala vengono risolti solo a questo livello, quando il metodo è già stato suddiviso in Metodo & Firme di NullaryMethod.

Di conseguenza diventa chiaro che il tipo di risultato può essere solo ottenere utilizzando la ricorsione:

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 
    } 

Dove impl è la classe che possiede il metodo e symbol è quello che si è ottenuto dalla Type.member(s) dalla scala riflessione

+0

Detto questo, spero ancora che il metodo in Scala e la riflessione Java possano essere resi più interoperabili, poiché entrambi hanno il loro vantaggio: il metodo java è molto più veloce da invocare, ma il suo controllo in JVM è abbastanza lento, non c'è perfezione qui: - < – tribbloid

4

ci sono due appr oaches che posso suggerire:

1) Rivela tipo generico da classe base:

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) Aggiungere metodo implicito che restituisce Tipo:

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

Ho paura nella maggior parte dei miei casi GenericExample è sconosciuto e sepolto in profondità in un grafo arbitrario di ereditarietà, quindi non è possibile scrivere un compagno Detector, ma proverò a groking la prima soluzione, grazie mille per la tua risposta! – tribbloid

+0

Non funziona: Errore: (72, 20) non trovato: valore interno val baseType = internal.thisType (classType) .baseType (baseClassType). Sto usando Scala 2.10.6 per compatibilità con una libreria – tribbloid

+0

Penso di avere un indizio ora, MethodSymbol della classe super generica cancella sempre i parametri di tipo, per aggirarlo devo invece usare MethodType. – tribbloid

Problemi correlati