Can I get the name of a method parameter using Java reflection?

If I have a class like this:

public class Whatever { public void aMethod(int aParam); } 

is there any way to find out that aMethod using a parameter called aParam which is of type int ?

+85
java reflection
Feb 10 2018-10-10T10
source share
14 answers

Summarizing:

  • getting parameter names is possible if debugging information is included during compilation. See this answer for more details.
  • otherwise, retrieving parameter names is not possible
  • getting parameter type is possible using method.getParameterTypes()

There are several options for writing autocomplete functions for the editor (as indicated in one of the comments):

  • use arg0 , arg1 , arg2 etc.
  • use intParam , stringParam , objectTypeParam etc.
  • use a combination of the above - the first for non-primitive types, and the second for primitive types.
  • argument names are not displayed at all - only types.
+67
Feb 10 2018-10-10
source share

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 Whatever class, 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 passed the -parameters argument -parameters 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> <compilerArgument>-parameters</compilerArgument> <testCompilerArgument>-parameters</testCompilerArgument> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> 

For more information, see the following links:

+60
Dec 15 '13 at 12:48
source share

To solve this problem, the Paranamer library was created.

He tries to determine method names in several ways. If the class was compiled with debugging, it can retrieve information by reading the bytecode of the class.

Another way is to insert a private static member into the bytecode of the class after compiling it, but before it is placed in the 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 in the end I worked with it. I hope to report problems to the maintainer.

+13
Feb 22 '10 at 16:20
source share

You can get a method with reflection and define its argument types. Check out http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29

However, you cannot specify the name of the argument used.

+6
Feb 10 2018-10-10
source share

Yes.
The code must be compiled with a compiler compatible with Java 8 , with the ability to save the formal names of the parameters included (option -parameters ).
Then this piece of code 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()); } } 
+5
Sep 02 '15 at 8:41
source share

While this is not possible (as others illustrate), you can use the annotation to transfer the parameter name and get this reflection.

Not the cleanest solution, but it does its job. Some web services actually do this to preserve parameter names (i.e.: deploy WSs with glass fish).

+4
Apr 16 2018-12-12T00:
source share

Spring MVC 3 may also do this, but I didn’t take the time to see how.

Matching method parameter names for URI template variable names is only possible if your code is compiled with debugging enabled. If you have not debugging enabled, you must specify the template name URI variable name in the @PathVariable annotation to bind the allowed value of the variable name to the method parameter. For example:

Adapted from spring documentation

+3
Feb 11 '10 at 9:08
source share

See java.beans.ConstructorProperties , this is an annotation designed to do just that.

+3
Jan 15 '13 at 15:15
source share

So you should be able to:

 Whatever.declaredMethods .find { it.name == 'aMethod' } .parameters .collect { "$it.type : $it.name" } 

But you will probably get a list like this:

 ["int : arg0"] 

I believe this will be fixed in Groovy 2.5+

So currently the answer is:

  • If this is the Groovy class, then no, you cannot get a name, but you should be able to in the future.
  • If this is a Java class compiled for Java 8, you should be able to.

See also:




For each method, something like:

 Whatever.declaredMethods .findAll { !it.synthetic } .collect { method -> println method method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';') } .each { println it } 
+3
Mar 03 '16 at 11:30
source share

see org.springframework.core.DefaultParameterNameDiscoverer class

 DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class)); 
+1
Sep 09 '14 at 14:12
source share

Parameter names are only useful for the compiler. When the compiler creates the class file, the parameter names are not included - the argument list of the method consists only of the number and types of its arguments. Thus, it would be impossible to get the parameter name using reflection (as noted in your question) - it does not exist anywhere.

However, if using reflection is not a strict requirement, you can get this information directly from the source code (if you have one).

0
Feb 10 2018-10-10
source share

To add my 2 cents; parameter information is available in the debug class file when you use javac -g to compile the source. And it is available for APT, but you need an annotation that you don't need. (Someone discussed something similar 4-5 years ago here: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )

In general, a short circuit cannot be obtained unless you work directly with the source files (similar to what APT does at compile time).

0
Feb 10 '10 at 16:00
source share

if you use eclipse see image below so that the compiler can store information about method parameters

enter image description here

0
May 23 '16 at 12:34
source share

As @Bozho explained, this is possible if debugging information is included during compilation. There is a good answer ...

How to get parameter names of object constructors (reflection)? @AdamPaynter

... using the ASM library. I put together an example showing how you can achieve your goal.

First of all, start with pom.xml with these dependencies.

 <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm-all</artifactId> <version>5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> 

Then this class should do what you want. Just call the static method getParameterNames() .

 import org.objectweb.asm.ClassReader; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.LocalVariableNode; import org.objectweb.asm.tree.MethodNode; public class ArgumentReflection { /** * Returns a list containing one parameter name for each argument accepted * by the given constructor. If the class was compiled with debugging * symbols, the parameter names will match those provided in the Java source * code. Otherwise, a generic "arg" parameter name is generated ("arg0" for * the first argument, "arg1" for the second...). * * This method relies on the constructor class loader to locate the * bytecode resource that defined its class. * * @param theMethod * @return * @throws IOException */ public static List<String> getParameterNames(Method theMethod) throws IOException { Class<?> declaringClass = theMethod.getDeclaringClass(); ClassLoader declaringClassLoader = declaringClass.getClassLoader(); Type declaringType = Type.getType(declaringClass); String constructorDescriptor = Type.getMethodDescriptor(theMethod); String url = declaringType.getInternalName() + ".class"; InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url); if (classFileInputStream == null) { throw new IllegalArgumentException( "The constructor class loader cannot find the bytecode that defined the constructor class (URL: " + url + ")"); } ClassNode classNode; try { classNode = new ClassNode(); ClassReader classReader = new ClassReader(classFileInputStream); classReader.accept(classNode, 0); } finally { classFileInputStream.close(); } @SuppressWarnings("unchecked") List<MethodNode> methods = classNode.methods; for (MethodNode method : methods) { if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) { Type[] argumentTypes = Type.getArgumentTypes(method.desc); List<String> parameterNames = new ArrayList<String>(argumentTypes.length); @SuppressWarnings("unchecked") List<LocalVariableNode> localVariables = method.localVariables; for (int i = 1; i <= argumentTypes.length; i++) { // The first local variable actually represents the "this" // object if the method is not static! parameterNames.add(localVariables.get(i).name); } return parameterNames; } } return null; } } 

Here is an example with unit test.

 public class ArgumentReflectionTest { @Test public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException { List<String> parameterNames = ArgumentReflection .getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class)); assertEquals("firstName", parameterNames.get(0)); assertEquals("lastName", parameterNames.get(1)); assertEquals(2, parameterNames.size()); } public static final class Clazz { public void callMe(String firstName, String lastName) { } } } 

You can find the complete github example.

Warnings

  • I slightly modified the original solution from @AdamPaynter so that it works for methods. If I understood correctly, his solution only works with designers.
  • This solution does not work with static methods. This is because the number of arguments returned by ASM is different, but it is something that can be easily fixed.
0
Jan 04 '17 at 12:04 on
source share



All Articles