views:

5871

answers:

3

Using Java Reflection, is it possible to get the name of a local variable? For example, if I have this:

Foo b = new Foo();
Foo a = new Foo();
Foo r = new Foo();

is it possible to implement a method that can find the names of those variables, like so:

public void baz(Foo... foos)
{
    for (Foo foo: foos) {
        // Print the name of each foo - b, a, and r
        System.out.println(***); 
    }
}
+16  A: 

It is not possible at all. Variable names aren't communicated within Java (and might also be removed due to compiler optimizations).

EDIT (related to comments):

If you step back from the idea of having to use it as function parameters, here's an alternative (which I wouldn't use - see below):

public void printFieldNames(Object obj, Foo... foos) {
    List<Foo> fooList = Arrays.asList(foos);
    for(Field field : obj.getClass().getFields()) {
         if(fooList.contains(field.get()) {
              System.out.println(field.getName());
         }
    }
}

There will be issues if a == b, a == r, or b == r or there are other fields which have the same references.

EDIT now unnecessary since question got clarified

Marcel J.
Then how do you explain this: http://java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html ?
Outlaw Programmer
-1: I think you've misunderstood. @David is after fields, not local variables. Local variables are indeed unavailable through the Reflection API.
Pourquoi Litytestdata
I think Pourquoi Litytestdata is right. Obviously fields can't be optimized away, so Marcel J. must be thinking of local variables.
Michael Myers
@David: You need to edit to clarify that you meant fields rather than local variables. The original question gives code that declares b, a, and r as local variables.
Jason S
please don't penalize someone for not reading minds when the original poster meant one thing but asked something else. :(
Jason S
I agree that he was talking about fields, but the method he provides as an example clearly states that he will use them as parameters like in `baz(b,a,r);` which simply won't work.
Marcel J.
@Jason S: He said both "instance variable" and "field". Those declarations could be local variables, or they could be package-private fields, or he could have been making them up on the spot and simply forgot the access modifiers. They don't _directly_ contradict the actual question as asked.
Michael Myers
I did mean local variables, and I've edited the question to reflect that. I thought it might not be possible to get variable names, but I figured I'd ask SO before considering it impossible.
David
Downvote rescinded. Apologies for misunderstanding and thanks for clarifying.
Pourquoi Litytestdata
Took back my -1 as well.
Outlaw Programmer
+6  A: 

(Edit: two previous answers removed, one for answering the question as it stood before edits and one for being, if not absolutely wrong, at least close to it.)

If you compile with debug information on (javac -g), the names of local variables are kept in the .class file. For example, take this simple class:

class TestLocalVarNames {
    public String aMethod(int arg) {
        String local1 = "a string";
        StringBuilder local2 = new StringBuilder();
        return local2.append(local1).append(arg).toString();
    }
}

After compiling with javac -g:vars TestLocalVarNames.java, the names of local variables are now in the .class file. javap's -l flag ("Print line number and local variable tables") can show them.

javap -l -c TestLocalVarNames shows:

class TestLocalVarNames extends java.lang.Object{
TestLocalVarNames();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       LTestLocalVarNames;

public java.lang.String aMethod(int);
  Code:
   0:   ldc     #2; //String a string
   2:   astore_2
   3:   new     #3; //class java/lang/StringBuilder
   6:   dup
   7:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
   10:  astore_3
   11:  aload_3
   12:  aload_2
   13:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  iload_1
   17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   23:  areturn

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      24      0    this       LTestLocalVarNames;
   0      24      1    arg       I
   3      21      2    local1       Ljava/lang/String;
   11      13      3    local2       Ljava/lang/StringBuilder;
}

The VM spec explains what we're seeing here:

§4.7.9 The LocalVariableTable Attribute:

The LocalVariableTable attribute is an optional variable-length attribute of a Code (§4.7.3) attribute. It may be used by debuggers to determine the value of a given local variable during the execution of a method.

The LocalVariableTable stores the names and types of the variables in each slot, so it is possible to match them up with the bytecode. This is how debuggers can do "Evaluate expression".

As erickson said, though, there's no way to access this table through normal reflection. If you're still determined to do this, I believe the Java Platform Debugger Architecture (JPDA) will help (but I've never used it myself).

Michael Myers
Uh-oh, erickson posted while I was editing and now I'm contradicting him. Which probably means I'm wrong.
Michael Myers
By default, `javac` puts a local variable table in the class for each method to assist debugging. Use the `-l` option to `javap` to see the local variable table.
erickson
Not by default, it seems. I had to use `javac -g:vars` to get it. (I've been trying to edit this answer for the past three hours, but as I said my network connection is having problems, which makes it hard to research.)
Michael Myers
You're right, sorry about that. It's line numbers that are "on" by default.
erickson
+8  A: 

It's not possible with Java reflection.

However, this information is often stored in class files. One compile-time optimization is to remove it, saving space (and providing some obsfuscation). However, when it is is present, each method has a local variable table attribute that lists the type and name of local variables, and the range of instructions where they are in scope.

Perhaps a byte-code engineering library like ASM would allow you to inspect this information at runtime. The only reasonable place I can think of for needing this information is in a development tool, and so byte-code engineering is likely to be useful for other purposes too.

erickson