【问题标题】:scala reflection: getDeclaringTrait?scala反射:getDeclaringTrait?
【发布时间】:2010-12-22 11:16:34
【问题描述】:

当我研究一个新库时,我有时会发现很难找到一个方法的实现。

在 Java 中,Metho#getDeclaringClass 提供声明给定方法的类。因此,通过迭代 Class#getMethods,我可以为每个方法找到声明它的类。

在 Scala 中,特征被转换为 Java 接口,扩展特征的类将通过将特征的方法转发给静态定义这些方法的同伴类来实现特征的方法。这意味着 Method#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)] ,其中每个元组都是一个方法和它声明的特征/类?

【问题讨论】:

    标签: reflection scala


    【解决方案1】:

    在 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
    

    我想按照这条路,您可以为 Scala 构建反射 API。

    【讨论】:

      【解决方案2】:

      下面是一些可行的方法:

      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' 的情况没有区别。

      【讨论】:

        【解决方案3】:

        如果您的目标实际上是“研究 [ing] 一个新库”,那么文档会为您提供这些信息。继承的方法(未重写)在定义它们的继承类下列出并链接(仅它们的名称)。

        这还不足以理解库吗?此外,每个文档页面都包含指向源代码的链接。

        【讨论】:

        • 您假设每个库都有很好的文档记录。此外,我的问题是关于一般的反思,而不是“我如何研究新图书馆”。这部分是为了让人们不会开始问“你为什么需要这样的反思?”。
        • 确定您询问的内容(并且您在第一句话中确实说过您正在研究新库)在源代码中可靠且完整地可用,因此在 Scaladoc HTML 中可用。此外,鉴于 Scala 和 JVM 之间关系的性质,通过源代码可以获得比通过字节码更多的信息。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多