如何使用反射在Java 8中获取方法参数名称?

时间:2014-01-30 11:37:12

标签: java reflection java-8 method-parameters

Java 8能够使用Reflection API获取方法参数名称。

  1. 如何获取这些方法参数名称?

  2. 据我所知,类文件不存储正式的参数名称。如何使用反射来获取这些?

4 个答案:

答案 0 :(得分:46)

  

如何获取这些方法参数名称?

基本上,您需要:

  • 获取对Class
  • 的引用
  • Class,通过调用getDeclaredMethod()Method来获取对getDeclaredMethods()的引用,该引用会返回对Method个对象的引用
  • Method对象调用(从Java 8开始的新版本)getParameters(),它返回Parameter个对象的数组
  • Parameter对象上,拨打getName()
Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
   System.err.println(m.getName());
   for (Parameter p : m.getParameters()) {
      System.err.println("  " + p.getName());
   }
}

输出:

...
indexOf
  arg0
indexOf
  arg0
  arg1
...

  

另据我所知,.class文件不存储形式参数。那我怎么能用反射来得到它们呢?

请参阅Parameter.getName()的javadoc:

  

... 如果参数的名称存在,则此方法将返回类文件提供的名称。 否则,此方法合成argN 形式的名称,其中N是声明参数的方法的描述符中参数的索引。

JDK是否支持此功能,是特定于实现的(正如您在上面的输出中看到的那样,JDK 8的build 125不支持它)。类文件格式支持可选属性,这些属性可由特定JVM / javac实现使用,并且被不支持它的其他实现忽略。

请注意,您甚至可以使用arg0arg1生成以上输出,并使用Java 8之前的JVM - 所有您需要知道的是可通过{{1访问的参数计数}}:

Method.getParameterTypes()

JDK 8的新增功能是,JVM有一个扩展的API和可能性来提供真实参数名称而不是Class<String> clz = String.class; for (Method m : clz.getDeclaredMethods()) { System.err.println(m.getName()); int paramCount = m.getParameterTypes().length; for (int i = 0; i < paramCount; i++) { System.err.println(" arg" + i); } } ,{ {1}},...

可以通过可选属性支持这些可选功能,可以附加到各种类文件结构。有关类文件中的arg0结构,请参阅4.6. Methods。另请参阅JVM规范中的4.7.1. Defining and Naming New Attributes

由于使用JDK 8,类文件版本将增加到52,因此还可以更改文件格式本身以支持此功能。

有关更多信息和实施备选方案,另请参阅JEP 118: Access to Parameter Names at Runtime。建议的实现模型是添加一个存储参数名称的可选属性。由于类文件格式已经支持这些可选属性,因此甚至可以通过某种方式实现这一类,以便较旧的JVM仍然可以使用类文件,它们根据规范的要求被忽略:

  

Java虚拟机实现需要静默忽略它们无法识别的属性。

<强>更新

正如@assylias所建议的,源需要使用arg1命令行选项method_info进行编译,以便将参数名称反射的元数据添加到类文件中。但是,这当然只会影响使用此选项编译的代码 - 上面的代码仍将打印javac-parameters等,因为运行时库不是使用此标志编译的,因此不包含类文件中的必要条目。

答案 1 :(得分:14)

感谢Andreas,但最后我从Method Parameters上获得了oracle Tutorials的完整解决方案

它说,

  

您可以获取任何方法或形式参数的名称   该方法的构造函数   java.lang.reflect.Executable.getParameters。 (方法和方法   构造函数扩展了类Executable,因此继承了   方法Executable.getParameters。)但是,.class文件不存储   默认情况下形式参数名称。这是因为很多工具   生成和使用类文件可能不会期望更大的静态和   包含参数名称的.class文件的动态足迹。在   特别是,这些工具必须处理更大的.class文件,以及   Java虚拟机(JVM)将使用更多内存。此外,   某些参数名称(如机密或密码)可能会公开   有关安全敏感方法的信息。

     

将正式参数名称存储在特定的.class文件中,从而   启用Reflection API以检索形式参数名称,编译   带有javac编译器的-parameters选项的源文件

如何编译

<强> Remember to compile the with the -parameters compiler option

预期输出(完整示例请访问上述链接)

<强> java MethodParameterSpy ExampleMethods

  

此命令打印以下内容:

Number of constructors: 1

Constructor #1
public ExampleMethods()

Number of declared constructors: 1

Declared constructor #1
public ExampleMethods()

Number of methods: 4

Method #1
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
             Return type: boolean
     Generic return type: boolean
         Parameter class: class java.lang.String
          Parameter name: stringParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false
         Parameter class: int
          Parameter name: intParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

Method #2
public int ExampleMethods.varArgsMethod(java.lang.String...)
             Return type: int
     Generic return type: int
         Parameter class: class [Ljava.lang.String;
          Parameter name: manyStrings
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

Method #3
public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>)
             Return type: boolean
     Generic return type: boolean
         Parameter class: interface java.util.List
          Parameter name: listParam
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

Method #4
public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>)
             Return type: void
     Generic return type: void
         Parameter class: class [Ljava.lang.Object;
          Parameter name: a
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false
         Parameter class: interface java.util.Collection
          Parameter name: c
               Modifiers: 0
            Is implicit?: false
        Is name present?: true
           Is synthetic?: false

答案 2 :(得分:6)

根据Store information about method parameters (usable via reflection) in intellij 13,Eclipse IDE中“javac -parameters”的等价物是“在Window中存储有关方法参数的信息(可通过反射使用)” - &gt;偏好 - &gt; Java - &gt;编译器。

答案 3 :(得分:5)

您可以使用Paranamer lib(https://github.com/paul-hammant/paranamer

适用于我的示例代码:

import com.thoughtworks.paranamer.AnnotationParanamer;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;

Paranamer info = new CachingParanamer(new AnnotationParanamer(new BytecodeReadingParanamer()));

Method method = Foo.class.getMethod(...);
String[] parameterNames = info.lookupParameterNames(method);

如果您使用Maven,请将此依赖项放在pom.xml中:

<dependency>
    <groupId>com.thoughtworks.paranamer</groupId>
    <artifactId>paranamer</artifactId>
    <version>2.8</version>
</dependency>