Скала отражение: getDeclaringTrait? - PullRequest
7 голосов
/ 02 декабря 2009

Когда я исследую новую библиотеку, мне иногда трудно найти реализацию метода.

В Java Metho # getDeclaringClass предоставляет класс, который объявил данный метод. Поэтому, перебирая класс # getMethods, я могу найти для каждого метода класс, который его объявил.

В Scala черты преобразуются в интерфейсы Java, и класс, расширяющий черту, реализует методы черты, перенаправляя их в сопутствующий класс, определяющий эти методы статически. Это означает, что метод # getDeclaringClass вернет класс, а не черту:

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

Как лучше всего обойти это? Имеется в виду, для данного класса, как я могу получить List [(Method, Class)], где каждый кортеж является методом и признаком / классом, в котором он был объявлен?

Ответы [ 3 ]

6 голосов
/ 03 декабря 2009

В Scala 2.8 вы можете использовать ScalaSigParser для анализа информации о байтовом коде, специфичном для scala.

Это будет более стабильно, чем формат сериализации байтового кода для признаков, классов и методов 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

Печать:

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

Полагаю, следуя этому пути, вы можете создать API отражения для Scala.

1 голос
/ 03 декабря 2009

Вот что-то вроде работы:

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

Вы можете видеть, что есть некоторая фильтрация, которая может быть сделана и, возможно, некоторые преобразования. Самое неприятное, что B имеет объявление 'hi', потому что оно перенаправляет вызов A $ class # hi. Однако это неотличимо от случая, когда B фактически переопределяет «hi» своей собственной реализацией.

0 голосов
/ 02 декабря 2009

Если ваша цель на самом деле «исследовать [новую] библиотеку», документация предоставит вам эту информацию. Унаследованные методы (не переопределенные) перечислены и связаны (только их имена) в унаследованном классе, который их определяет.

Разве этого недостаточно для понимания библиотеки? Также каждая страница документации содержит ссылку на исходный код.

...