2009-12-02 56 views
7

當我研究一個新的庫時,我有時會發現很難找到方法的實現。scala反射:getDeclaringTrait?

在Java中,Metho#getDeclaringClass提供了聲明給定方法的類。因此,通過迭代Class#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 

我想下面這條路上,你可以建立一個斯卡拉反射API。

0

如果你的目標實際上是「研究一個新的圖書館」,文檔給你提供這些信息。繼承的方法(未覆蓋)在定義它們的繼承類下列出並鏈接(僅限它們的名稱)。

這是不足以理解庫的目的?另外,每個文檔頁面都包含一個指向源代碼的鏈接。

+1

你假設每個圖書館都有詳細記錄。另外,我的問題是關於一般的反思,而不是「我該如何研究新的圖書館」。那部分是讓人們不會開始問「爲什麼你需要這樣的反思?」。 – IttayD 2009-12-02 17:51:00

+0

確定你詢問的事情(你在第一句話中曾經說過你正在研究新的庫)可靠並且完全地在源代碼中提供,因此在Scaladoc HTML中也是可用的。此外,鑑於Scala和JVM之間關係的性質,通過源可以獲得更多的信息,而不是通過字節碼。 – 2009-12-02 18:41:19

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有'hi'聲明,因爲它將呼叫轉發給A $ class#hi。然而,這與B實際上用自己的實現覆蓋'hi'的情況沒有區別。