it-swarm.com.de

Java Reflection: Wie erhalte ich den Namen einer Variablen?

Ist es mit Java Reflection möglich, den Namen einer lokalen Variablen abzurufen? Beispiel:

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

ist es möglich, eine Methode zu implementieren, die die Namen dieser Variablen wie folgt findet:

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

EDIT: Diese Frage unterscheidet sich von Gibt es eine Möglichkeit in Java den Namen der Variablen zu finden, die an eine Funktion übergeben wurde? insofern stellt es eher die Frage, ob man den Namen einer lokalen Variablen mit Reflektion bestimmen kann, während sich die andere Frage (einschließlich der akzeptierten Antwort) mehr auf das Testen von Variablenwerten konzentriert.

128
David Koelle

Ab Java 8 sind einige lokale Variablennameninformationen über Reflection verfügbar. Weitere Informationen finden Sie im Abschnitt "Update" weiter unten.

Vollständige Informationen werden häufig in Klassendateien gespeichert. Eine Optimierung zur Kompilierungszeit besteht darin, sie zu entfernen, um Platz zu sparen (und etwas Verschleierung bereitzustellen). Wenn es jedoch vorhanden ist, verfügt jede Methode über ein Tabellenattribut für lokale Variablen, in dem Typ und Name der lokalen Variablen sowie der Bereich der Anweisungen aufgelistet sind, in dem sie enthalten sind.

Vielleicht können Sie diese Informationen mit einer Bytecode-Engineering-Bibliothek wie ASM zur Laufzeit überprüfen. Der einzige vernünftige Ort, an dem ich mir vorstellen kann, diese Informationen zu benötigen, ist ein Entwicklungstool, sodass die Bytecode-Entwicklung wahrscheinlich auch für andere Zwecke nützlich ist.


Update: Eingeschränkte Unterstützung für dieses war hinzugefügt zu Java 8. Parameter (ein spezielles Klasse lokaler Variablennamen) sind jetzt über Reflection verfügbar, was unter anderem dazu beitragen kann, @ParameterName Anmerkungen, die von Abhängigkeitsinjektionscontainern verwendet werden.

62
erickson

Es ist überhaupt nicht möglich . Variablennamen werden nicht innerhalb von Java (und möglicherweise auch aufgrund von Compiler-Optimierungen entfernt) mitgeteilt.

BEARBEITEN (bezogen auf Kommentare):

Wenn Sie von der Idee zurücktreten, sie als Funktionsparameter verwenden zu müssen, finden Sie hier eine Alternative (die ich nicht verwenden würde - siehe unten):

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

Es wird Probleme geben, wenn a == b, a == r, or b == r oder es gibt andere Felder, die die gleichen Referenzen haben.

BEARBEITEN jetzt unnötig, da die Frage geklärt wurde

48

( Bearbeiten: Zwei vorherige Antworten wurden entfernt, eine für die Beantwortung der Frage in der vor der Bearbeitung gestellten Form und eine für die Tatsache, dass sie, wenn nicht absolut falsch, zumindest in der Nähe liegt. )

Wenn Sie mit Debug-Informationen kompilieren (javac -g) werden die Namen der lokalen Variablen in der .class-Datei gespeichert. Nehmen Sie zum Beispiel diese einfache Klasse:

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

Nach dem Kompilieren mit javac -g:vars TestLocalVarNames.Java, die Namen der lokalen Variablen befinden sich jetzt in der .class-Datei. javap 's -l flag ("Zeilennummer und lokale Variablentabellen drucken") kann sie anzeigen.

javap -l -c TestLocalVarNames zeigt an:

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

Die VM-Spezifikation erklärt, was wir hier sehen:

§4.7.9 Das LocalVariableTable Attribut :

Das Attribut LocalVariableTable ist ein optionales Attribut variabler Länge eines Attributs Code (§4.7.3). Es kann von Debuggern verwendet werden, um den Wert einer bestimmten lokalen Variablen während der Ausführung einer Methode zu bestimmen.

Das LocalVariableTable speichert die Namen und Typen der Variablen in jedem Slot, so dass es möglich ist, sie mit dem Bytecode abzugleichen. So können Debugger "Ausdruck auswerten".

Wie erickson jedoch sagte, gibt es keine Möglichkeit, durch normale Reflektion auf diesen Tisch zuzugreifen. Wenn Sie immer noch entschlossen sind, dies zu tun, wird meiner Meinung nach das Java Platform Debugger Architecture (JPDA) helfen (aber ich habe es selbst nie verwendet).

29
Michael Myers
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));
  }
 }

}
14
Peeter

Das können Sie machen:

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

Alles was Sie tun müssen, ist ein Array von Feldern zu erstellen und es dann auf die gewünschte Klasse zu setzen, wie unten gezeigt.

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

Zum Beispiel, wenn Sie es getan haben

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

du würdest bekommen

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
0