首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用反射在Java 8中获取方法参数名称?

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

Stack Overflow用户
提问于 2014-01-30 11:37:11
回答 4查看 38.9K关注 0票数 42

Java 8具有使用反射API获取方法参数名称的能力。

  1. 如何获得这些方法参数名称?
  2. 据我所知,类文件不存储正式的参数名称。我怎样才能用反射得到这些呢?
EN

回答 4

Stack Overflow用户

发布于 2014-01-30 12:00:06

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

基本上,你需要:

  • 获取对Class的引用
  • Class中,通过调用getDeclaredMethod()getDeclaredMethods()来获取对Method的引用,后者返回对Method对象的引用。
  • Method对象调用(Java8的新的) getParameters(),它返回Parameter对象的数组
  • Parameter对象上,调用getName()
代码语言:javascript
复制
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());
   }
}

输出:

代码语言:javascript
复制
...
indexOf
  arg0
indexOf
  arg0
  arg1
...

据我所知,也不存储正式参数.class文件。那么如何使用反射来获得它们呢?

参见Parameter.getName()的javadoc

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

JDK是否支持这一点,是特定于实现的(正如您从上面的输出中可以看到,构建125的JDK 8不支持它)。类文件格式支持可选属性,这些属性可以由特定的JVM/javac实现使用,而其他不支持它的实现则忽略这些属性。

请注意,您甚至可以使用arg0arg1,.生成上述输出。使用前Java 8 JVMs您需要知道的就是参数计数,它可以通过Method.getParameterTypes()访问

代码语言:javascript
复制
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的新特点是有一个扩展的API和可能性,用于JVM提供实参名称,而不是arg0arg1、.

可以通过可选属性支持这些可选特性,这些属性可以附加到各种类文件结构中。有关类文件中的4.6。方法结构,请参见method_info。还请参阅JVM规范中的4.7.1.定义和命名新属性

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

有关更多信息和实现备选方案,请参见JEP 118:在运行时访问参数名称。所提出的实现模型是添加一个可选属性来存储参数名。由于类文件格式已经支持这些可选属性,这甚至在某种程度上是可能的,这样类文件仍然可以被旧的JVM使用,在那里它们被忽略,这是规范所要求的:

Java虚拟机实现需要默默地忽略它们不识别的属性。

更新

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

票数 50
EN

Stack Overflow用户

发布于 2014-01-31 05:18:56

谢谢安德烈亚斯,但我终于从甲骨文方法参数教程中得到了完整的解决方案。

上面写着,

您可以使用java.lang.reflect.Executable.getParameters方法获取任何方法或构造函数的形式参数的名称。(类方法和构造函数扩展了类可执行性,因此继承了方法Executable.getParameters。)但是,默认情况下,.class文件不存储正式参数名称。这是因为许多生成和使用类文件的工具可能不会期望包含参数名称的.class文件具有更大的静态和动态占用空间。特别是,这些工具必须处理更大的.class文件,而(JVM)将使用更多的内存。此外,某些参数名称(如秘密或密码)可能公开有关安全敏感方法的信息。 将正式参数名存储在特定的.class文件中,从而使反射API能够检索正式的参数名称,并使用-parameters选项将源文件编译到javac编译器

如何编译

Remember to compile the with the -parameters compiler option

预期输出(完整的示例请访问上面提到的链接)

java MethodParameterSpy ExampleMethods

此命令打印以下内容:

代码语言:javascript
复制
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
票数 15
EN

Stack Overflow用户

发布于 2016-04-29 04:25:05

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

为我工作的示例代码:

代码语言:javascript
复制
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中:

代码语言:javascript
复制
<dependency>
    <groupId>com.thoughtworks.paranamer</groupId>
    <artifactId>paranamer</artifactId>
    <version>2.8</version>
</dependency>
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21455403

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档