7 Stimmen

scala reflection: getDeclaringTrait?

Wenn ich in einer neuen Bibliothek recherchiere, finde ich es manchmal schwierig, die Implementierung einer Methode zu finden.

In Java liefert Metho#getDeclaringClass die Klasse, die eine bestimmte Methode deklariert hat. Durch Iteration über Class#getMethods kann ich also für jede Methode die Klasse finden, die sie erklärt hat.

In Scala werden Traits in Java-Schnittstellen umgewandelt und eine Klasse, die einen Trait erweitert, implementiert die Methoden des Traits, indem sie sie an eine Begleitklasse weiterleitet, die diese Methoden statisch definiert. Das bedeutet, dass Method#getDeclaringClass die Klasse zurückgibt, nicht den Trait:

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

Wie kann man das am besten umgehen? Das heißt, angesichts einer Klasse, wie kann ich eine Liste[(Methode, Klasse)] erhalten, wo jedes Tupel eine Methode und die Eigenschaft/Klasse ist, in der es deklariert wurde?

6voto

Thomas Jung Punkte 31524

In Scala 2.8 können Sie die ScalaSigParser um die Scala-spezifischen Bytecode-Informationen zu analysieren.

Dies wird stabiler sein als das Bytecode-Serialisierungsformat von Scala Traits, Klassen und Methoden.

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

Drucke:

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

Ich nehme an, dass Sie auf diesem Weg eine Reflection-API für Scala erstellen können.

1voto

IttayD Punkte 26802

Hier ist etwas, das irgendwie funktioniert:

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

Wie Sie sehen, kann man einige Filterungen vornehmen und vielleicht einige Konvertierungen vornehmen. Das Ärgerlichste ist, dass B eine Deklaration von "hi" hat, weil es den Aufruf an A$class#hi weiterleitet. Dies ist jedoch nicht von dem Fall zu unterscheiden, in dem B tatsächlich 'hi' mit seiner eigenen Implementierung überschreibt.

0voto

Randall Schulz Punkte 26072

Wenn Ihr Ziel tatsächlich die "Erforschung einer neuen Bibliothek" ist, gibt Ihnen die Dokumentation diese Informationen. Vererbte Methoden (nicht überschrieben) werden aufgelistet und verlinkt (nur ihre Namen) unter der vererbten Klasse, die sie definiert.

Reicht das nicht aus, um die Bibliothek zu verstehen? Außerdem enthält jede Dokumentationsseite einen Link zum Quellcode.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X