Tutorial :scala reflection: getDeclaringTrait?


When I research a new library, I sometimes find it hard to locate the implementation of a method.

In Java, Metho#getDeclaringClass provides the class that declared a given method. So by iterating over Class#getMethods, I can find for each method, the class that declared it.

In Scala, traits are converted to Java interfaces and a class that extends a trait will implement the methods of the trait by forwarding them to a companion class defining these methods statically. This means, that Method#getDeclaringClass will return the class, not the 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  

What is the best way to work around this? Meaning, given a class, how can I get a List[(Method, Class)] where each tuple is a method and the trait/class it was declared in?


In Scala 2.8 you can use the ScalaSigParser to parse the scala specific byte code information.

This will be more stable than the byte code serialization format of scala traits, classes and methods.

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  

I suppose following this road you can build a reflection API for Scala.


Here's something that sort-of-works:

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  

You can see there's some filtering that can be done and maybe some conversions. The most annoying thing is that B has a declaration of 'hi', because it forwards the call to A$class#hi. However, this is indistinguishable from the case where B actually overrides 'hi' with its own implementation.


If your goal is actually "research[ing] a new library," the documentation gives you this information. Inherited methods (not overridden) are listed and linked (their names only) under the inherited class that defines them.

Is this not sufficient for the purposes of understanding the libary? Also, each documentation page includes a link to the source code.

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »