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 { 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++) {
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.
danidemi Jan 04 '17 at 12:04 on 2017-01-04 12:04
source share