views:

1168

answers:

8

Hello,
I'm writing small and very DRY framework, which heavily relies on metadata. I'd like to know if there is a way to obtain method parameter names, i.e. given some method

public void a(int myIntParam, String theString) { ... }

get the strings "myIntParam" and "theString".

I know I could annotate parameters, but that wouldn't be nice...

public void a(
    @Param("myIntParam") int myIntParam,
    @Param("theString") String theString
) { ... }
+7  A: 

I could be wrong about this... but I don't think parameter names appear in a class file so I would guess that there is no way to get them via reflection.

bobwienholt
+2  A: 

@bobwienholt is correct - parameter names are not compiled into java classes, and so aren't available at runtime.

Dan Vinton
+10  A: 

Not really, but codehaus have this library that will do for a lot of purposes: http://paranamer.codehaus.org/

GaryF
Blimey, I had no idea about this thing... I guess you learn something every day!
Dan Vinton
Very interesting thing.. But I don't want to introduce any dependences, and the way it operates is rather complicated.
AnSGri
A: 

Parameter names are available through apt (now part of javac).

Tom Hawtin - tackline
@Tom: do you still need to explicitly annotate classes to get apt to preprocess them? Or is a more general-purpose precompiler now?
Dan Vinton
I believe annotation processors can ask to receive all classes whether they have annotations or not. It's not really a preprocessor as the original classes are left alone, although other classes and artifacts can be created.
Tom Hawtin - tackline
+2  A: 

The name of the parameters are present in the class file, when the java code was compiled with debugging information (via the -g option). The class file then contains a LocalVariableTable attribute (see http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#5956). This contains the names of local variables and parameters are just local variables. The parameters correspond to the variable slots starting at index 1 (or index 0 for static methods).

ralfs
+8  A: 

Here is a dirty solution that needs some tweaking. Maybe someone can make it better.

Cons:

  • Requires that you know the location of compiled class file.
  • It has to be compiled with the -g flag.

Code:

import com.sun.org.apache.bcel.internal.classfile.ClassParser;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.LocalVariable;
import com.sun.org.apache.bcel.internal.classfile.Method;
import java.io.IOException;

public class Main {

  public static void main(String[] args) throws IOException {
      ClassParser parser = new ClassParser("Main.class");
      JavaClass clazz = parser.parse();

      for (Method m : clazz.getMethods()) {
          System.out.println("Method: " + m.getName());
          int size = m.getArgumentTypes().length;
          if (!m.isStatic()) {
            size++;
          }

          for (int i = 0; i < size; i++) {
              LocalVariable variable = m.getLocalVariableTable().getLocalVariable(i);
              System.out.println("  - Param: " + variable.getName());
          }
      }
  }

  public void a(int myIntParam, String theString) {
  }
}

Output:

$ javac -g Main.java
$ java Main
Method: <init>
- Param: this
Method: main
- Param: args
Method: a
- Param: this
- Param: myIntParam
- Param: theString

+2  A: 

We created a custom annotation for the method that holds a String[] of parameter names. This approach felt a little easier to manage than having to annotate each individual parameter. We plan to add build-time checking that the number of annotated parameter names matches the number of arguments, since that it what we require.

dstine
Very good idea! I'll definitely convert all those @Param to single @Params.
AnSGri
A: 

If you are using Spring you are in luck. Just add this to your applicationContext.xml:

<bean class="org.springframework.core.LocalVariableTableParameterNameDiscoverer"/>

Then you can inject this bean where it is needed:

@Autowired
private ParameterNameDiscoverer parameterNameDiscoverer;

Method m = ...
String[] names = parameterNameDiscoverer.getParameterNames(m);

As its name suggests, this implementation relies on classes being compiled with debug info.

David Tinker