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

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

  1. 我怎样才能得到这些方法参数名称?

  2. 据我所知,类文件不存储forms参数名称。 我怎样才能得到这些使用reflection?

我怎样才能得到这些方法参数名称(一些示例代码)?

基本上,你需要:

  • 得到一个Class的引用
  • Class ,通过调用getDeclaredMethod()getDeclaredMethods()来获得对Method的引用,该Method返回对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文件不存储forms参数。那么我怎样才能让他们使用reflection?

查看Parameter.getName()的javadoc:

如果参数的名称存在 ,则此方法返回由类文件提供的名称否则 ,这个方法合成一个forms为argN的名字 ,其中N是声明该参数的方法的描述符中的参数的索引。

JDK是否支持这个function,是特定于实现的(如上面的输出所示,JDK 8的版本125不支持)。 类文件格式支持可选的属性,这些属性可以被特定的JVM / javac实现使用,而其他不支持的实现则忽略它们。

请注意,您甚至可以使用arg0arg1 ,…和Java 8 JVM生成上面的输出 – 您只需要知道可通过Method.getParameterTypes()访问的参数数量:

 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); } } 

JDK 8的新function是有一个扩展的API,JVM可以提供真实的参数名称而不是arg0arg1 ,…

通过可以附加到各种类文件结构的可选属性支持这种可选特征是可能的。 见4.6。 类文件中method_info结构的方法 。 另见4.7.1。 在JVM规范中定义和命名新的属性 。

由于使用JDK 8,类文件版本将增加到52,也可以自行更改文件格式以支持此function。

另请参阅JEP 118:在运行时访问参数名称以获取更多信息和实施scheme。 所提出的实现模型是添加一个可选属性来存储参数名称。 由于类文件格式已经支持这些可选的属性,所以甚至可以这样做,以便类文件仍然可以被较旧的JVM使用,在那里它们根据规范的要求被简单地忽略:

Java虚拟机实现需要静静地忽略他们不认识的属性。

更新

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

感谢Andreas,但最后我从Oracle Tutorials on Method Parameters上得到了完整的解决scheme

它说,

您可以使用java.lang.reflect.Executable.getParameters方法获取任何方法或构造方法的形参的名称。 (类Method和构造函数扩展了类Executable,因此inheritance了Executable.getParameters方法。)但是,.class文件默认不存储forms参数名称。 这是因为许多生成和使用类文件的工具可能不会期望包含参数名称的.class文件的较大静态和dynamic覆盖区。 尤其是,这些工具必须处理较大的.class文件,Java虚拟机(JVM)将使用更多的内存。 另外,某些参数名称(如密码或密码)可能会暴露有关安全敏感方法的信息。

要将正式参数名称存储在特定的.class文件中,从而使Reflection API能够检索正式参数名称,请使用-parameters选项将源文件编译为javac编译器

如何编译

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 

你可以使用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> 

根据在intellij 13中存储有关方法参数的信息(可通过reflection使用), Eclipse IDE中的“javac -parameters”的等价物是在Window – > Preferences – > Java中存储有关方法参数的信息(可通过reflection使用) >编译器。

Interesting Posts