【问题标题】:How to get Method Parameter names in Java 8 using reflection?如何使用反射在 Java 8 中获取方法参数名称?
【发布时间】:2014-01-30 11:37:11
【问题描述】:

Java 8 能够使用反射 API 获取方法参数名称。

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

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

【问题讨论】:

    标签: java reflection java-8 method-parameters


    【解决方案1】:

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

    基本上,您需要:

    • 获取对Class 的引用
    • Class,通过调用getDeclaredMethod()getDeclaredMethods() 获取对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 文件不存储形式参数。那么我怎样才能使用反射来获取它们呢?

    查看Parameter.getName()的javadoc:

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

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

    请注意,您甚至可以使用 Java 8 之前的 JVM 使用 arg0arg1... 生成上述输出 - 您只需要知道可通过 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 提供 实际参数名称 而不是 arg0arg1 , ...

    通过可附加到各种类文件结构的可选属性,可以支持此类可选功能。有关类文件中的 method_info 结构,请参见 4.6. Methods。另请参阅 JVM 规范中的 4.7.1. Defining and Naming New Attributes

    从 JDK 8 开始,class 文件版本将增加到 52,也可以更改文件格式本身以支持此功能。

    另请参阅JEP 118: Access to Parameter Names at Runtime 了解更多信息和实施替代方案。建议的实现模型是添加一个存储参数名称的可选属性。由于类文件格式已经支持这些可选属性,这甚至可以通过某种方式使类文件仍然可以被较旧的 JVM 使用,在这种情况下,它们会按照规范的要求被简单地忽略:

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

    更新

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

    【讨论】:

    • 仍然不清楚类文件如何存储方法参数名称。
    • 查看 JVM 规范 - 类文件支持可选属性
    • 我问的是 java8,你提到的是 jvm/se7 的链接
    • 您需要使用-parameter 标志进行编译。
    • @assylias 这个标志被命名为-parameters(以's'结尾)
    【解决方案2】:

    您可以使用 Paranamer 库 (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>
    

    【讨论】:

      【解决方案3】:

      感谢 Andreas,但最后我从 Method Parameters 上的 oracle 教程中获得了完整的解决方案

      它说,

      您可以获取任何方法的形参名称或 带有方法的构造函数 java.lang.reflect.Executable.getParameters。 (类方法和 构造函数扩展了 Executable 类,因此继承了 方法 Executable.getParameters。)但是,.class 文件不存储 默认情况下形式参数名称。这是因为许多工具 生产和消费类文件可能不会期望更大的静态和 包含参数名称的 .class 文件的动态足迹。在 特别是,这些工具必须处理更大的 .class 文件,并且 Java 虚拟机 (JVM) 将使用更多内存。此外, 一些参数名称,例如秘密或密码,可能会暴露 有关安全敏感方法的信息。

      将形式参数名称存储在特定的 .class 文件中,因此 启用反射 API 以检索形式参数名称,编译 javac 编译器的带有 -parameters 选项的源文件

      如何编译

      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
      

      【讨论】:

        【解决方案4】:

        根据Store information about method parameters (usable via reflection) in intellij 13,Eclipse IDE 中“javac -parameters”的等效项是“在 Window -> Preferences -> Java -> Compiler 中存储有关方法参数的信息(可通过反射使用)”。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          • 1970-01-01
          • 2011-11-30
          • 1970-01-01
          • 1970-01-01
          • 2010-10-03
          相关资源
          最近更新 更多