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

 讨厌上学的-彭志超-_354 发布于 2023-01-30 13:40

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

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

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

Andreas Fest.. 46

我怎样才能获得这些方法参数名称?

基本上,你需要:

得到一个参考 Class

Class,得到一个参考Method致电getDeclaredMethod()getDeclaredMethods()返回到引用Method对象

Method对象中调用(Java 8中的new)getParameters(),它返回一个Parameter对象数组

Parameter对象上,打电话getName()

Class 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文件不存储形式参数.那我怎么能用反射来得到它们呢?

请参阅javadoc Parameter.getName():

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

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

请注意,您甚至可以使用arg0,arg1使用Java 8之前的JVM 生成上述输出- 您需要知道的是参数计数,可通过Method.getParameterTypes()以下方式访问:

Class 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中提供真正的参数名称代替arg0,arg1...

通过可附加到各种类文件结构的可选属性,可以支持这些可选功能.见4.6.方法为method_info一个类文件内的结构.另见4.7.1.在JVM规范中定义和命名新属性.

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

另请参阅JEP 118:在运行时访问参数名称以获取更多信息和实现备选方案.建议的实现模型是添加一个存储参数名称的可选属性.由于类文件格式已经支持这些可选属性,因此甚至可以通过某种方式实现类文件,以便较旧的JVM仍然可以使用它们,它们根据规范的要求被简单地忽略:

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

更新

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

4 个回答
  • 我怎样才能获得这些方法参数名称?

    基本上,你需要:

    得到一个参考 Class

    Class,得到一个参考Method致电getDeclaredMethod()getDeclaredMethods()返回到引用Method对象

    Method对象中调用(Java 8中的new)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文件不存储形式参数.那我怎么能用反射来得到它们呢?

    请参阅javadoc Parameter.getName():

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

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

    请注意,您甚至可以使用arg0,arg1使用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是,有一个扩展API和可能性为JVM中提供真正的参数名称代替arg0,arg1...

    通过可附加到各种类文件结构的可选属性,可以支持这些可选功能.见4.6.方法为method_info一个类文件内的结构.另见4.7.1.在JVM规范中定义和命名新属性.

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

    另请参阅JEP 118:在运行时访问参数名称以获取更多信息和实现备选方案.建议的实现模型是添加一个存储参数名称的可选属性.由于类文件格式已经支持这些可选属性,因此甚至可以通过某种方式实现类文件,以便较旧的JVM仍然可以使用它们,它们根据规范的要求被简单地忽略:

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

    更新

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

    2023-01-30 13:42 回答
  • 你可以使用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>
    

    2023-01-30 13:42 回答
  • 感谢Andreas,但最后我从oracle Tutorials on Method Parameters获得了完整的解决方案

    它说,

    您可以使用方法java.lang.reflect.Executable.getParameters获取任何方法或构造函数的形式参数的名称.(Method和Constructor类扩展了类Executable,因此继承了Executable.getParameters方法.)但是,.class文件默认不存储形式参数名.这是因为许多生成和使用类文件的工具可能不会期望包含参数名称的.class文件的更大静态和动态占用空间.特别是,这些工具必须处理更大的.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
    

    2023-01-30 13:42 回答
  • 根据intellij 13中关于方法参数(可通过反射使用)的商店信息,Eclipse IDE中"javac -parameters"的等价物是"在窗口 - >首选项 - > Java中存储有关方法参数的信息(可通过反射使用)" - >编译器.

    2023-01-30 13:42 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有