2009-12-02 30 views
7

Khi tôi nghiên cứu một thư viện mới, đôi khi tôi thấy khó xác định vị trí thực hiện phương thức.phản ánh scala: getDeclaringTrait?

Trong Java, Metho # getDeclaringClass cung cấp lớp đã khai báo một phương thức đã cho. Vì vậy, bằng cách lặp qua lớp # getMethods, tôi có thể tìm cho mỗi phương thức, lớp đã khai báo nó. Trong Scala, các đặc tính được chuyển đổi sang các giao diện Java và một lớp mở rộng một đặc điểm sẽ thực hiện các phương thức của đặc điểm bằng cách chuyển tiếp chúng đến một lớp đồng hành xác định các phương thức này một cách tĩnh. Điều này có nghĩa là phương thứC# getDeclaringClass sẽ trả về lớp, không phải là đặc điểm:

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 

Cách tốt nhất để giải quyết vấn đề này là gì? Có nghĩa là, cho một lớp, làm thế nào tôi có thể nhận được một List [(Method, Class)] trong đó mỗi tuple là một method và đặc điểm/class nó đã được khai báo trong đó?

Trả lời

6

Trong Scala 2.8, bạn có thể sử dụng ScalaSigParser để phân tích thông tin mã byte cụ thể của scala.

Điều này sẽ ổn định hơn định dạng tuần tự mã byte của các đặc điểm, lớp và phương thức 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 

Prints:

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 

Tôi giả sử sau con đường này bạn có thể xây dựng một API phản chiếu cho Scala.

0

Nếu mục tiêu của bạn thực sự là "nghiên cứu [ing] một thư viện mới", tài liệu sẽ cung cấp cho bạn thông tin này. Các phương thức kế thừa (không ghi đè) được liệt kê và liên kết (chỉ tên của chúng) trong lớp kế thừa xác định chúng.

Điều này không đủ cho mục đích hiểu thư viện? Ngoài ra, mỗi trang tài liệu bao gồm một liên kết đến mã nguồn.

+1

bạn giả định rằng mọi thư viện đều được ghi lại tài liệu. và bên cạnh đó, câu hỏi của tôi là về sự phản chiếu nói chung, không phải là "làm thế nào tôi có thể nghiên cứu các thư viện mới". phần đó là để mọi người sẽ không bắt đầu hỏi "tại sao bạn cần sự phản chiếu như vậy?". – IttayD

+0

Xác định những điều bạn hỏi (và bạn đã nói trong câu đầu tiên bạn đang nghiên cứu vào thư viện mới) có sẵn đáng tin cậy và hoàn toàn trong mã nguồn và do đó trong HTML Scaladoc. Hơn nữa, do tính chất của mối quan hệ giữa Scala và JVM, nhiều thông tin hơn sẽ luôn sẵn có thông qua nguồn thông qua mã bytecode. –

1

Dưới đây là một cái gì đó mà loại-of-công trình:

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ạn có thể thấy có một số bộ lọc có thể được thực hiện và có thể một số chuyển đổi. Điều khó chịu nhất là B có tuyên bố 'hi', bởi vì nó chuyển tiếp cuộc gọi đến lớp $ # hi. Tuy nhiên, điều này là không thể phân biệt được từ trường hợp B thực sự ghi đè 'hi' bằng cách thực hiện riêng của nó.