Can I obtain method parameter name using Java reflection?
If I have a class like this:
public class Whatever
{
public void aMethod(int aParam);
}
is there any way to know that aMethod
uses a parameter named aParam
, that is of type int
?
In Java 8 you can do the following:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
public final class Methods {
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
private Methods(){}
}
So for your class Whatever
we can do a manual test:
import java.lang.reflect.Method;
public class ManualTest {
public static void main(String[] args) {
Method[] declaredMethods = Whatever.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("aMethod")) {
System.out.println(Methods.getParameterNames(declaredMethod));
break;
}
}
}
}
which should print [aParam]
if you have passed -parameters
argument to your Java 8 compiler.
For Maven users:
<properties>
<!-- PLUGIN VERSIONS -->
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<!-- OTHER PROPERTIES -->
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<!-- Original answer -->
<compilerArgument>-parameters</compilerArgument>
<!-- Or, if you use the plugin version >= 3.6.2 -->
<parameters>true</parameters>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
For more information see following links:
- Official Java Tutorial: Obtaining Names of Method Parameters
- JEP 118: Access to Parameter Names at Runtime
- Javadoc for Parameter class
To summarize:
- getting parameter names is possible if debug information is included during compilation. See this answer for more details
- otherwise getting parameter names is not possible
- getting parameter type is possible, using
method.getParameterTypes()
For the sake of writing autocomplete functionality for an editor (as you stated in one of the comments) there are a few options:
- use
arg0
,arg1
,arg2
etc. - use
intParam
,stringParam
,objectTypeParam
, etc. - use a combination of the above - the former for non-primitive types, and the latter for primitive types.
- don't show argument names at all - just the types.
The Paranamer library was created to solve this same problem.
It tries to determine method names in a few different ways. If the class was compiled with debugging it can extract the information by reading the bytecode of the class.
Another way is for it to inject a private static member into the bytecode of the class after it is compiled, but before it is placed in a jar. It then uses reflection to extract this information from the class at runtime.
https://github.com/paul-hammant/paranamer
I had problems using this library, but I did get it working in the end. I'm hoping to report the problems to the maintainer.
see org.springframework.core.DefaultParameterNameDiscoverer class
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Yes.
Code must be compiled with Java 8 compliant compiler with option to store formal parameter names turned on (-parameters option).
Then this code snippet should work:
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());
}
}