2009-04-13 84 views
110

使用Java反射,是否可以獲取本地變量的名稱?舉例來說,如果我有這樣的:Java反射:如何獲取變量的名稱?

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

是有可能實現能夠找到這些變量的名稱,像這樣的方法:

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

編輯:這個問題是從Is there a way in Java to find the name of the variable that was passed to a function?不同因爲它更純粹地提出了一個問題,即是否可以使用反射來確定局部變量的名稱,而另一個問題(包括被接受的答案)則更側重於測試變量的值。

+9

所有好的答案!感謝大家的回覆和評論 - 這已經是一次有趣而深刻的討論。 – 2009-04-14 15:52:09

+0

我發現[詳細反思教程](http://omtlab.com/java-reflection-tutorial/) – 2012-03-16 18:07:37

+0

這是可能的。看我的[gist] [1]。適用於JDK 1.1到JDK 7. [1]:https://gist.github.com/2011728 – 2012-04-11 07:05:38

回答

57

從Java 8開始,通過反射可以獲得一些本地變量名稱信息。請參閱下面的「更新」部分。

完整的信息通常存儲在類文件中。一個編譯時優化是刪除它,節省空間(並提供一些混淆)。但是,當它存在時,每種方法都有一個局部變量表屬性,它列出局部變量的類型和名稱以及它們在範圍內的指令範圍。

也許像ASM這樣的字節碼工程庫會允許您在運行時檢查此信息。我所能想到的需要這些信息的唯一合理位置就是在開發工具中,所以字節碼工程也可能對其他用途有用。


更新:這個有限的支持是added to Java 8.參數(一類特殊的局部變量)的名稱現在是通過反射可用。除此之外,這可以幫助取代依賴注入容器使用的註釋。

47

它是而不是可能。變量名不在Java內傳遞(並且由於編譯器優化,也可能會被刪除)。

EDIT(相關評論):

如果您不必使用它作爲函數參數的想法退一步,這裏是一個另類(我不會用 - 見下文):

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()); 
     } 
    } 
} 

如果a == b, a == r, or b == r或有其他字段有相同的參考文獻,會有問題。

編輯現在就不必要了,因爲問題得到了澄清

+0

那麼你如何解釋這個:http://java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html? – 2009-04-13 15:22:49

28

編輯:前兩個答案刪除,一個爲回答這個問題,因爲它編輯之前站着一個爲是,如果不是完全錯誤的,至少接近

如果使用(javac -g)上的調試信息編譯,局部變量的名稱保存在.class文件中。例如,利用這個簡單的類:

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

javac -g:vars TestLocalVarNames.java編譯後,局部變量的名字現在在.class文件。 javap-l標誌(「打印行號和局部變量表」)可以顯示它們。

javap -l -c TestLocalVarNames顯示:

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; 
} 

VM spec解釋了我們現在看到的:

§4.7.9 The LocalVariableTable Attribute

LocalVariableTable屬性是Code的可選可變長度屬性( §4.7.3)屬性。調試器可能會使用它來確定在執行方法期間給定局部變量的值。

LocalVariableTable在每個插槽中存儲變量的名稱和類型,因此可以將它們與字節碼進行匹配。調試器可以執行「評估表達式」。

正如erickson所說,雖然沒有辦法通過正常的反射來訪問這張表。如果你仍然決定這樣做,我相信Java Platform Debugger Architecture (JPDA)會有幫助(但我從來沒有用過它)。

12
import java.lang.reflect.Field; 


public class test { 

public int i = 5; 

public Integer test = 5; 

public String omghi = "der"; 

public static String testStatic = "THIS IS STATIC"; 

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException { 
    test t = new test(); 
    for(Field f : t.getClass().getFields()) { 
    System.out.println(f.getGenericType() +" "+f.getName() + " = " + f.get(t)); 
    } 
} 

} 
0

所有你需要做的就是創建一個字段數組,然後將其設置爲你想要的類,如下所示。

Field fld[] = (class name).class.getDeclaredFields(); 
for(Field x : fld) 
{System.out.println(x);} 

舉例來說,如果你沒有

Field fld[] = Integer.class.getDeclaredFields(); 
      for(Field x : fld) 
      {System.out.println(x);} 

你會得到

public static final int java.lang.Integer.MIN_VALUE 
public static final int java.lang.Integer.MAX_VALUE 
public static final java.lang.Class java.lang.Integer.TYPE 
static final char[] java.lang.Integer.digits 
static final char[] java.lang.Integer.DigitTens 
static final char[] java.lang.Integer.DigitOnes 
static final int[] java.lang.Integer.sizeTable 
private static java.lang.String java.lang.Integer.integerCacheHighPropValue 
private final int java.lang.Integer.value 
public static final int java.lang.Integer.SIZE 
private static final long java.lang.Integer.serialVersionUID 
-1

看看這個例子:

PersonneTest pt=new PersonneTest(); 
System.out.println(pt.getClass().getDeclaredFields().length); 
Field[]x=pt.getClass().getDeclaredFields(); 
System.out.println(x[1].getName()); 
6

你可以這樣做:

Field[] fields = YourClass.class.getDeclaredFields(); 
//gives no of fields 
System.out.println(fields.length);   
for (Field field : fields) { 
    //gives the names of the fields 
    System.out.println(field.getName()); 
}