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

如何在Java8中使用反射获取方法参数名?,java,reflection,java-8,method-parameters,Java,Reflection,Java 8,Method Parameters,Java8能够使用反射API获取方法参数名称 如何获取这些方法参数名 据我所知,类文件不存储正式的参数名。如何使用反射获取这些信息 如何获取这些方法参数名称? 基本上,您需要: 获取对类的引用 从类中,通过调用或getDeclaredMethods()获取对方法的引用,该方法返回对方法对象的引用 从方法对象中,调用(从Java 8开始新增)getParameters(),它返回一个参数对象数组 在参数对象上,调用getName() 据我所知,类文件不存储形式参数。那么,如何使用反射获取它们

Java8能够使用反射API获取方法参数名称

  • 如何获取这些方法参数名

  • 据我所知,类文件不存储正式的参数名。如何使用反射获取这些信息

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

    基本上,您需要:

    • 获取对
      类的引用
    • 中,通过调用或
      getDeclaredMethods()
      获取对
      方法
      的引用,该方法返回对
      方法
      对象的引用
    • 方法
      对象中,调用(从Java 8开始新增)
      getParameters()
      ,它返回一个
      参数
      对象数组
    • 参数
      对象上,调用
      getName()

    据我所知,类文件不存储形式参数。那么,如何使用反射获取它们?

    请参阅javadoc以了解:

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

    JDK是否支持这一点取决于具体的实现(从上面的输出可以看出,JDK 8的build 125不支持它)。类文件格式支持可选属性,这些属性可由特定JVM/javac实现使用,其他不支持它的实现会忽略这些属性

    请注意,您甚至可以使用
    arg0
    arg1
    。。。对于Java 8之前的JVM,您只需要知道参数计数,该计数可通过
    方法访问。getParameterTypes()

    Class clz=String.Class;
    对于(方法m:clz.getDeclaredMethods()){
    System.err.println(m.getName());
    int paramCount=m.getParameterTypes().length;
    对于(int i=0;i
    JDK 8的新功能是有一个扩展的API,JVM可以提供真实参数名,而不是
    arg0
    arg1

    可以通过附加到各种类文件结构的可选属性来支持这些可选特性。有关类文件中的
    方法\u info
    结构,请参阅。另请参见JVM规范中的

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

    有关更多信息和实现备选方案,请参见。建议的实现模型是添加一个可选属性来存储参数名。由于类文件格式已经支持这些可选属性,这在某种程度上甚至是可能的,以便旧JVM仍然可以使用这些类文件,而这些类文件只是根据规范的要求被忽略:

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

    更新


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

    谢谢Andreas,但最终我从oracle教程中获得了完整的解决方案

    上面说,

    您可以获取任何方法或方法的形式参数的名称 方法的构造函数 java.lang.reflect.Executable.getParameters。(类和方法) 构造函数扩展类的可执行文件,从而继承 但是,.class文件不存储 默认情况下,形式参数名。这是因为许多工具 生成和使用类文件可能不需要更大的静态和 包含参数名称的.class文件的动态示意图。在里面 特别是,这些工具必须处理更大的.class文件,以及 Java虚拟机(JVM)将使用更多内存。此外 某些参数名称(如secret或password)可能会暴露 有关安全敏感方法的信息

    将正式参数名存储在特定的.class文件中,从而 使反射API能够检索正式参数名,并编译 javac编译器的带有-parameters选项的源文件

    如何编译
    记住使用-parameters编译器选项编译

    预期输出(如需完整示例,请访问上述链接)
    java方法参数间谍示例方法

    此命令打印以下内容:

    施工人员人数:1人
    构造函数#1
    公共示例方法()
    声明的构造函数数:1
    声明构造函数#1
    公共示例方法()
    方法数目:4
    方法#1
    公共布尔ExampleMethods.simpleMethod(java.lang.String,int)
    返回类型:boolean
    泛型返回类型:boolean
    参数类:class java.lang.String
    参数名称:stringParam
    修改器:0
    是隐式的吗?:假
    有名字吗?:对
    是人工合成的吗
    参数类:int
    参数名称:intParam
    修改器:0
    是隐式的吗?:假
    有名字吗?:对
    是人工合成的吗
    方法#2
    平民的
    
    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<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);
      }
    }
    
    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
    
    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);
    
    <dependency>
        <groupId>com.thoughtworks.paranamer</groupId>
        <artifactId>paranamer</artifactId>
        <version>2.8</version>
    </dependency>