2009-12-02 4 views
7

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

В 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)], где каждый кортеж является методом и признаком/классом, в котором он был объявлен?

ответ

6

В 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 

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

0

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

Этого недостаточно для понимания личности? Кроме того, на каждой странице документации есть ссылка на исходный код.

+1

Вы считаете, что каждая библиотека хорошо документирована. и, кроме того, мой вопрос касается размышлений в целом, а не «как я могу исследовать новые библиотеки». эта часть была так, чтобы люди не стали спрашивать «зачем вам такое отражение?». – IttayD

+0

Устанавливая то, о чем вы спрашиваете (и вы сказали в своем первом предложении, что вы проводите исследования в новых библиотеках), надежно и полностью доступны в исходном коде и, следовательно, в HTML-коде Scaladoc. Кроме того, учитывая характер отношений между Scala и JVM, дополнительная информация всегда будет доступна через источник, а не через байт-код. –

1

Вот что-то, что-то-оф-работы:

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 имеет объявление «привет», потому что он переадресует вызов A $ class # hi. Однако это неотличимо от случая, когда B фактически переопределяет «привет» своей собственной реализацией.

Смежные вопросы