如何在 Scala/Java 方法中通过反射获取参数名称和类型?

发布于 2024-12-07 14:12:18 字数 643 浏览 3 评论 0原文

我们可以使用反射来获取方法名称,如下所示:

object Foo { def bar(name:String, age:Int) = {} } 
val foo = Foo.getClass
val methods = foo.getMethods.filter(_.getName.startsWith("b"))
methods.foreach(m => println(m.getName))

我现在需要获取参数类型和名称。

  • 参数名称是否存储在字节码中?如果答案是肯定的,如何访问它们?
  • 如果上面的答案是否定的,我们可以使用注释以某种方式存储名称吗?
  • 有人可以举个例子来阅读这些类型以及如何使用它们。我只对具有 String 和/或 Array[String] 类型参数的函数感兴趣。

[编辑:] Java 版本的解决方案也可以。

[编辑:]注释似乎是一种方法。然而,Scala 注释支持并不是那么好。 相关问题

We can use reflection to get method names as follows:

object Foo { def bar(name:String, age:Int) = {} } 
val foo = Foo.getClass
val methods = foo.getMethods.filter(_.getName.startsWith("b"))
methods.foreach(m => println(m.getName))

I now need to get the parameter types and names.

  • Are the parameter names stored in the byte-code? If answer is yes, how to access them?
  • If answer above is no, can we store the names somehow using annotations?
  • Can someone given an example to read the types, and how to use them. I am interested only in functions having String and/or Array[String] type parameters.

[EDIT:] Java version of the solution also ok.

[EDIT:] Annotations seems to be one way to do it. However, Scala annotation support is not that good. Related SO question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

寄人书 2024-12-14 14:12:18

我还没有尝试过,但 http://paranamer.codehaus.org/ 是为此任务而设计的。

I've not tried it, but http://paranamer.codehaus.org/ is designed for this task.

败给现实 2024-12-14 14:12:18

Java 的字节码规范不要求存储参数名称。但是,它们可以通过调试符号潜入(如果编译器被告知生成它们)。我知道 ASM 字节码库会读取这些符号(如果存在)。请参阅我对“如何获取对象构造函数的参数名称” 查找构造函数参数名称的 Java 示例(在字节码中,构造函数只是名称为)。

Java's bytecode specification doesn't require the parameter names to be stored. However, they can sneak in via the debugging symbols (if the compiler was told to generate them). I know that the ASM bytecode library reads these symbols if they are present. See my answer to "How to get the parameter names of an object's constructors" for a Java example of finding constructor parameter names (in bytecode, constructors are just methods whose name is <init>).

陌伤浅笑 2024-12-14 14:12:18

如果类中存在调试信息,则可以按如下方式完成。

我基本上使用 Adam Paynter 的回答 并复制粘贴 此处稍作编辑后让它在 Scala 中工作。

package test
import java.io.InputStream
import java.util.ArrayList
import scala.collection.JavaConversions._
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.LocalVariableNode
import org.objectweb.asm.tree.MethodNode

object Util {
  case class Param(paraName:String, paraType:Type)
  case class ScalaMethod(name:String, returnType:Type, params:List[Param])

  def main(args:Array[String]):Unit = {
    getMethods(scala.util.Random.getClass).foreach(printMethod _ )
    def printMethod(m:ScalaMethod) = {
      println (m.name+" => "+m.returnType.getClassName)
      m.params.foreach(p =>
        println (" "+ p.paraName+":"+p.paraType.getClassName))
    }
  }

  /**
   * extracts the names, parameter names and parameter types of all methods of c
   */
  def getMethods(c:Class[_]):List[ScalaMethod] = {
    val cl:ClassLoader = c.getClassLoader();
    val t:Type = Type.getType(c);
    val url:String = t.getInternalName() + ".class";
    val is:InputStream = cl.getResourceAsStream(url);
    if (is == null)
      throw new IllegalArgumentException("""The class loader cannot
                                         find the bytecode that defined the
                                         class (URL: " + url + ")""");
    val cn = new ClassNode();
    val cr = new ClassReader(is);
    cr.accept(cn, 0);
    is.close();
    val methods = cn.methods.asInstanceOf[java.util.List[MethodNode]];
    var mList:List[ScalaMethod] = Nil
    if (methods.size > 0) for (i <- 1 to methods.size) {
      val m:MethodNode = methods.get(i-1)
      val argTypes:Array[Type] = Type.getArgumentTypes(m.desc);
      val paraNames = new java.util.ArrayList[String](argTypes.length)
      val vars = m.localVariables.asInstanceOf[java.util.List[LocalVariableNode]];
      var pList:List[Param] = Nil
      if (argTypes.length > 0) for (i <- 0 to argTypes.length) {
          // The first local variable actually represents the "this" object
          paraNames.add(vars.get(i).name);
          pList = Param(paraNames.get(i-1), argTypes(i-1)) :: pList
      }
      mList = ScalaMethod(m.name, Type.getReturnType(m.desc), pList) :: mList
    }
    mList
  }
}

If debugging info is present in the classes, it can be done as follows.

I am basically using Adam Paynter's answer and copy-pasting the code from here after slight edit to get it to work in Scala.

package test
import java.io.InputStream
import java.util.ArrayList
import scala.collection.JavaConversions._
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.LocalVariableNode
import org.objectweb.asm.tree.MethodNode

object Util {
  case class Param(paraName:String, paraType:Type)
  case class ScalaMethod(name:String, returnType:Type, params:List[Param])

  def main(args:Array[String]):Unit = {
    getMethods(scala.util.Random.getClass).foreach(printMethod _ )
    def printMethod(m:ScalaMethod) = {
      println (m.name+" => "+m.returnType.getClassName)
      m.params.foreach(p =>
        println (" "+ p.paraName+":"+p.paraType.getClassName))
    }
  }

  /**
   * extracts the names, parameter names and parameter types of all methods of c
   */
  def getMethods(c:Class[_]):List[ScalaMethod] = {
    val cl:ClassLoader = c.getClassLoader();
    val t:Type = Type.getType(c);
    val url:String = t.getInternalName() + ".class";
    val is:InputStream = cl.getResourceAsStream(url);
    if (is == null)
      throw new IllegalArgumentException("""The class loader cannot
                                         find the bytecode that defined the
                                         class (URL: " + url + ")""");
    val cn = new ClassNode();
    val cr = new ClassReader(is);
    cr.accept(cn, 0);
    is.close();
    val methods = cn.methods.asInstanceOf[java.util.List[MethodNode]];
    var mList:List[ScalaMethod] = Nil
    if (methods.size > 0) for (i <- 1 to methods.size) {
      val m:MethodNode = methods.get(i-1)
      val argTypes:Array[Type] = Type.getArgumentTypes(m.desc);
      val paraNames = new java.util.ArrayList[String](argTypes.length)
      val vars = m.localVariables.asInstanceOf[java.util.List[LocalVariableNode]];
      var pList:List[Param] = Nil
      if (argTypes.length > 0) for (i <- 0 to argTypes.length) {
          // The first local variable actually represents the "this" object
          paraNames.add(vars.get(i).name);
          pList = Param(paraNames.get(i-1), argTypes(i-1)) :: pList
      }
      mList = ScalaMethod(m.name, Type.getReturnType(m.desc), pList) :: mList
    }
    mList
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文