How to get Method Parameter names in Java 8 using reflection?
Asked Answered
F

4

42

Java 8 has the ability to acquire method parameter names using Reflection 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?

Forgetmenot answered 30/1, 2014 at 11:37 Comment(0)
C
50

How can i get these method parameter names?

Basically, you need to:

  • get a reference to a Class
  • From the Class, get a reference to a Method by calling getDeclaredMethod() or getDeclaredMethods() which returns references to Method objects
  • From the Method object, call (new as of Java 8) getParameters() which returns an array of Parameter objects
  • On the Parameter object, call 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. Then how can i get them using reflection?

See the javadoc for Parameter.getName():

... 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.

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

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():

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, ...

Supporting such optional features is possible through optional attributes which can be attached to the various class file structures. See 4.6. Methods for the method_info structure within a class file. See also 4.7.1. Defining and Naming New Attributes in the JVM spec.

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.

See also JEP 118: Access to Parameter Names at Runtime for more information and implementation alternatives. 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:

Java Virtual Machine implementations are required to silently ignore attributes they do not recognize.

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. 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.

Chladek answered 30/1, 2014 at 12:0 Comment(12)
Still,i m not clear as how can class file store method parameter names.Forgetmenot
See the JVM spec - class files support optional attributesChladek
I m asking about java8 and you are mentioning a link for jvm/se7Forgetmenot
Yes - as far as I found, there is no official Java 8 spec yet. Certainly the class file format will not be changed significantly, so the Java 7 spec should still be valid - at least Java 8 class files will still support optional attributes, and that is what is described in the linked chapter ... See also my updated answer for additional information.Chladek
In your link(In descsription heading), its written "The proposed approach is to create an optional new JVM attribute in version 52.0 class files to store information about the parameters of a JVM-level method."Forgetmenot
Also it s wriiten in dependencies that "In JDK components, fully implementing this feature requires coordinated compiler, libraries, and JVM changes."Forgetmenot
Note fully implementing. This is not even done in the latest JDK8 build, as you can see from the output of my sample code. And sure, supporting it requires a compiler which creates the necessary optional attribute entries in the class file, a jvm which can read the optional attribute during class loading and a runtime library which can retrieve the optional attribute and provide them to the Parameters objects.Chladek
You need to compile with the -parameter flag.Ruprecht
@Ruprecht Ah, thats what I still wanted to check, whether there is a command line parameter... thanks, will update the answerChladek
@Andreas Assylias is right, that is what i want to know...Thanks to you bothForgetmenot
@Ruprecht This flag is named -parameters (with 's' at the end)Dunfermline
This is a great answer. I've spent about a few hours, finding out, why this works in one application and doesn't in another. I've compared all libraries versions I have and dove into the core implementation of java.reflect methods, I've reached some native method that returned params in a working app and didn't in the application that was getting argument names argN. And just found an option -parameters that was set in a working app... Thank you so much.Prognosticate
F
15

Thanks Andreas, but finally i got the complete solution from 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. (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. 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. In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory. 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.

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
Forgetmenot answered 31/1, 2014 at 5:18 Comment(0)
D
7

as per Store information about method parameters (usable via reflection) in intellij 13, the equivalent of "javac -parameters" within the Eclipse IDE is 'Store information about method parameters (usable via reflection)' in Window -> Preferences -> Java -> Compiler.

Dialogism answered 19/8, 2015 at 21:9 Comment(0)
A
7

You can use Paranamer lib (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:

<dependency>
    <groupId>com.thoughtworks.paranamer</groupId>
    <artifactId>paranamer</artifactId>
    <version>2.8</version>
</dependency>
Ankle answered 29/4, 2016 at 4:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.