简体   繁体   English

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

[英]How to get Method Parameter names in Java 8 using reflection?

Java 8 has the ability to acquire method parameter names using Reflection API. Java 8 能够使用反射 API 获取方法参数名称。

  1. How can I get these method parameter names?如何获取这些方法参数名称?

  2. As per my knowledge, class files do not store formal parameter names.据我所知,类文件不存储形式参数名称。 How can I get these using reflection?如何使用反射获得这些?

How can i get these method parameter names?我如何获得这些方法参数名称?

Basically, you need to:基本上,您需要:

  • get a reference to a Class获取对Class的引用
  • From the Class , get a reference to a Method by calling getDeclaredMethod() or getDeclaredMethods() which returns references to Method objectsClass ,得到一个参考Method调用getDeclaredMethod()getDeclaredMethods()返回引用Method的对象
  • From the Method object, call (new as of Java 8) getParameters() which returns an array of Parameter objectsMethod对象中,调用(Java 8 中的新功能) getParameters() ,它返回一个Parameter对象数组
  • On the Parameter object, call getName()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());
   }
}

Output:输出:

...
indexOf
  arg0
indexOf
  arg0
  arg1
...

Also as per my knowledge .class files do not store formal parameter.另外据我所知 .class 文件不存储形式参数。 Then how can i get them using reflection?那我怎样才能使用反射得到它们呢?

See the javadoc for Parameter.getName() :请参阅Parameter.getName()的 javadoc:

... If the parameter's name is present , then this method returns the name provided by the class file . ...如果存在参数名称,则此方法返回类文件提供的名称 Otherwise , this method synthesizes a name of the form argN , where N is the index of the parameter in the descriptor of the method which declares the parameter.否则,此方法合成一个 argN 形式的名称,其中 N 是参数在声明参数的方法的描述符中的索引。

Whether a JDK supports this, is implementation specific (as you can see form the above output, build 125 of JDK 8 does not support it). JDK 是否支持这一点,是特定于实现的(正如您从上面的输出中看到的,JDK 8 的 build 125 不支持它)。 The class file format supports optional attributes which can be used by a specific JVM/javac implementation and which are ignored by other implementations which do not support it.类文件格式支持可选属性,这些属性可以由特定的 JVM/javac 实现使用,并且会被不支持它的其他实现忽略。

Note that you could even generate the above output with arg0 , arg1 , ... with pre Java 8 JVMs - all you need to know is the parameter count which is accessible through Method.getParameterTypes() :请注意,您甚至可以使用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);
  }
}

What is new with JDK 8 is that there is an extended API and the possibility for JVMs to provide the real parameter names instead of arg0 , arg1 , ...什么是新的与JDK 8是,有一个扩展API和可能性的JVM提供真正的参数名称,而不是arg0arg1 ,...

Supporting such optional features is possible through optional attributes which can be attached to the various class file structures.通过可以附加到各种类文件结构的可选属性,可以支持此类可选功能。 See 4.6.4.6。 Methods for the method_info structure within a class file.类文件中method_info结构的方法 See also 4.7.1.另见4.7.1。 Defining and Naming New Attributes in the JVM spec.在 JVM 规范中定义和命名新属性

Since with JDK 8, the class file version will be incremented to 52, it would also be possible to change the file format itself to support this feature.由于使用 JDK 8,类文件版本将增加到 52,因此也可以更改文件格式本身以支持此功能。

See also JEP 118: Access to Parameter Names at Runtime for more information and implementation alternatives.另请参阅JEP 118:在运行时访问参数名称以获取更多信息和实现替代方案。 The proposed implementation model is to add an optional attribute which stores the parameter names.建议的实现模型是添加一个存储参数名称的可选属性。 Since the class file format already supports these optional attributes, this would even be possible in a way so that the class files can still be used by older JVMs, where they are simply ignored as demanded by the spec:由于类文件格式已经支持这些可选属性,因此这甚至可以以某种方式实现,以便旧的 JVM 仍然可以使用类文件,而根据规范的要求,它们会被简单地忽略:

Java Virtual Machine implementations are required to silently ignore attributes they do not recognize. Java 虚拟机实现需要静默忽略它们无法识别的属性。

Update更新

As suggested by @assylias, the source needs to be compiled with the javac command line option -parameters in order to add the meta data for parameter name reflection to the class file.正如@assylias 所建议的,需要使用javac命令行选项-parameters编译源代码,以便将参数名称反射的元数据添加到类文件中。 However, this will of course only affect code compiled with this option - the code above will still print arg0 , arg1 etc. since the runtime libraries are not be compiled with this flag and hence do not contain the necessary entries in the class files.但是,这当然只会影响使用此选项编译的代码 - 上面的代码仍会打印arg0arg1等,因为运行时库未使用此标志编译,因此不包含类文件中的必要条目。

Thanks Andreas, but finally i got the complete solution from oracle Tutorials on Method Parameters谢谢 Andreas,但最后我从 oracle Tutorials on Method Parameters得到了完整的解决方案

It says,它说,

You can obtain the names of the formal parameters of any method or constructor with the method java.lang.reflect.Executable.getParameters.您可以使用 java.lang.reflect.Executable.getParameters 方法获取任何方法或构造函数的形参名称。 (The classes Method and Constructor extend the class Executable and therefore inherit the method Executable.getParameters.) However, .class files do not store formal parameter names by default. (类 Method 和 Constructor 扩展了 Executable 类,因此继承了 Executable.getParameters 方法。)但是,默认情况下 .class 文件不存储形式参数名称。 This is because many tools that produce and consume class files may not expect the larger static and dynamic footprint of .class files that contain parameter names.这是因为许多生成和使用类文件的工具可能不希望包含参数名称的 .class 文件有更大的静态和动态占用空间。 In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory.特别是,这些工具必须处理更大的 .class 文件,而 Java 虚拟机 (JVM) 将使用更多内存。 In addition, some parameter names, such as secret or password, may expose information about security-sensitive methods.此外,某些参数名称,例如机密或密码,可能会暴露有关安全敏感方法的信息。

To store formal parameter names in a particular .class file, and thus enable the Reflection API to retrieve formal parameter names, compile the source file with the -parameters option to the javac compiler .要将形式参数名称存储在特定的 .class 文件中,从而使反射 API 能够检索形式参数名称,请使用 -parameters 选项将源文件编译为 javac 编译器

How to Compile如何编译

Remember to compile the with the -parameters compiler option

Expected Output(For complete example visit the link mentioned above)预期输出(有关完整示例,请访问上面提到的链接)

java MethodParameterSpy ExampleMethods

This command prints the following:此命令打印以下内容:

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

You can use Paranamer lib ( https://github.com/paul-hammant/paranamer )您可以使用 Paranamer 库( https://github.com/paul-hammant/paranamer

Sample code that works for me:对我有用的示例代码:

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

If you use Maven then put this dependency in your pom.xml:如果您使用 Maven,则将此依赖项放在您的 pom.xml 中:

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

根据intellij 13 中有关方法参数的存储信息(可通过反射使用), Eclipse IDE 中“javac -parameters”的等价物是窗口 -> 首选项 -> Java 中的“存储有关方法参数的信息(可通过反射使用)” - > 编译器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM