2009-12-02 18 views
7

Cuando investigo una nueva biblioteca, a veces me resulta difícil localizar la implementación de un método.scala reflection: getDeclaringTrait?

En Java, Metho # getDeclaringClass proporciona la clase que declaró un método determinado. Entonces al iterar sobre Class # getMethods, puedo encontrar para cada método, la clase que lo declaró.

En Scala, los rasgos se convierten en interfaces Java y una clase que amplía un rasgo implementará los métodos del rasgo reenviándolos a una clase complementaria que define estos métodos estáticamente. Esto significa, que el método # getDeclaringClass devolverá la clase, no el rasgo:

scala> trait A { def foo = {println("hi")}} 
defined trait A 

scala> class B extends A 
defined class B 

scala> classOf[B].getMethods.find(_.getName() == "foo").get.getDeclaringClass 
res3: java.lang.Class[_] = class B 

¿Cuál es la mejor manera de evitar esto? Es decir, dada una clase, ¿cómo puedo obtener una Lista [(Método, Clase)] donde cada tupla es un método y el rasgo/clase en el que se declaró?

Respuesta

6

En Scala 2.8 puede usar el ScalaSigParser para analizar la información del código de bytes específico scala.

Esto será más estable que el formato de serialización de códigos de bytes de los rasgos, clases y métodos de scala.

import tools.scalap.scalax.rules.scalasig._ 
import scala.runtime._ 

val scalaSig = ScalaSigParser.parse(classOf[RichDouble]).get 
val richDoubleSymbol = scalaSig.topLevelClasses.head 
val methods = richDoubleSymbol.children filter (_ match { 
    case m : MethodSymbol => true 
    case _ => false 
}) 

methods foreach println 
richDoubleSymbol.isTrait 
ScalaSigParser.parse(classOf[Ordered[Any]]).get.topLevelClasses.head.isTrait 

Lienzo:

scala> methods foreach println 
MethodSymbol(x, owner=0, flags=20080004, info=23 ,None) 
MethodSymbol(<init>, owner=0, flags=200, info=33 ,None) 
[...] 
MethodSymbol(isPosInfinity, owner=0, flags=200, info=117 ,None) 
MethodSymbol(isNegInfinity, owner=0, flags=200, info=117 ,None) 

scala> richDoubleSymbol.isTrait 
res1: Boolean = false 

scala> ScalaSigParser.parse(classOf[Ordered[Any]]).get.topLevelClasses.head.isTrait 
res2: Boolean = true 

supongo que siguiendo este camino se puede construir una API de reflexión para la Scala.

0

Si su objetivo es en realidad "investigar [una] nueva biblioteca", la documentación le brinda esta información. Los métodos heredados (no reemplazados) se enumeran y vinculan (solo sus nombres) bajo la clase heredada que los define.

¿No es esto suficiente para entender la biblioteca? Además, cada página de documentación incluye un enlace al código fuente.

+1

supones que todas las bibliotecas están bien documentadas. y además, mi pregunta es sobre la reflexión en general, no sobre "cómo puedo investigar nuevas bibliotecas". esa parte fue para que la gente no empiece a preguntar "¿por qué necesitas tal reflexión?". – IttayD

+0

Al determinar las cosas que pregunta (y dijo en su primera oración que estaba investigando en nuevas bibliotecas) están disponibles de manera confiable y completa en el código fuente y, por lo tanto, en el código HTML de Scaladoc. Además, dada la naturaleza de la relación entre Scala y la JVM, siempre habrá más información disponible a través de la fuente que a través del bytecode. –

1

Aquí es algo que clase-de-obras:

def methods(c: Class[_]): Array[String] = { 
    val dm = try { 
     val cls = if (c.isInterface) Class.forName(c.getName() + "$class") else c 

     cls.getDeclaredMethods().map(m =>        
      decode(c.getCanonicalName) + "#" + 
      decode(m.getName) + "(" + 
      {m.getParameterTypes.toList match { 
       case h :: tail => tail.map{(c: Class[_]) => decode(c.getCanonicalName)}.mkString(",") 
       case Nil => "" 
      }} + "): " +  
      decode(m.getReturnType.getCanonicalName)) 
    } catch {case _ => Array[String]()} 

    dm ++ c.getInterfaces.flatMap(methods(_)) 
} 

scala> trait A {def hi = {println("hi")}} 
scala> class B extends A 
scala> methods(classOf[B]).foreach(println(_)) 
Main.$anon$1.B#$tag(): int 
Main.$anon$1.B#Main$$anon$A$$$outer(): Main.$anon$1 
Main.$anon$1.B#Main$$anon$B$$$outer(): Main.$anon$1 
Main.$anon$1.B#hi(): void 
Main.$anon$1.A#$init$(): void 
Main.$anon$1.A#hi(): void 
scala.ScalaObject#$tag(): int 
scala.ScalaObject#$init$(): void 

Se puede ver que hay algún tipo de filtrado que se puede hacer y tal vez algunas conversiones. Lo más molesto es que B tiene una declaración de 'hola', porque reenvía la llamada a A $ class # hi. Sin embargo, esto es indistinguible del caso en el que B realmente anula 'hola' con su propia implementación.