Вопрос-ответ

Java Reflection: How to get the name of a variable?

Отражение 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(***);
}
}

РЕДАКТИРОВАТЬ: Этот вопрос отличается от Есть ли в Java способ найти имя переменной, которая была передана функции? в том смысле, что оно более чисто задает вопрос о том, можно ли использовать отражение для определения имени локальной переменной, тогда как другой вопрос (включая принятый ответ) больше ориентирован на тестирование значений переменных.

Переведено автоматически
Ответ 1

Начиная с Java 8, некоторая информация об именах локальных переменных доступна через отражение. Смотрите раздел "Обновление" ниже.

Полная информация часто хранится в файлах классов. Одна из оптимизаций во время компиляции заключается в ее удалении, что экономит место (и обеспечивает некоторую запутанность). Однако, когда оно присутствует, каждый метод имеет атрибут таблицы локальных переменных, в котором перечислены тип и имя локальных переменных, а также диапазон инструкций, в которых они находятся в области видимости.

Возможно, инженерная библиотека байт-кода, такая как ASM, позволила бы вам проверять эту информацию во время выполнения. Единственное разумное место, которое я могу придумать для получения этой информации, - это инструмент разработки, и поэтому разработка байт-кода, вероятно, будет полезна и для других целей.


Обновление: В Java 8 была добавлена ограниченная поддержка для этого. Имена параметров (особый класс локальных переменных) теперь доступны через отражение. Среди прочих целей это может помочь заменить @ParameterName аннотации, используемые контейнерами внедрения зависимостей.

Ответ 2

Это вообще невозможно. Имена переменных не передаются в Java (и также могут быть удалены из-за оптимизации компилятора).

РЕДАКТИРОВАТЬ (связано с комментариями):

Если вы откажетесь от идеи использования ее в качестве параметров функции, вот альтернатива (которую я бы не стал использовать - см. Ниже):

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 или есть другие поля, которые имеют те же ссылки.

РЕДАКТИРОВАТЬ теперь не нужно, поскольку вопрос прояснен

Ответ 3

(Редактировать: удалены два предыдущих ответа, один за то, что он отвечал на вопрос в том виде, в котором он стоял до внесения изменений, и один за то, что он был если не абсолютно неправильным, то, по крайней мере, близким к нему.)

Если вы компилируете с отладочной информацией в (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's -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;
}

В спецификации виртуальной машины объясняется, что мы здесь видим:

§4.7.9 LocalVariableTable Атрибут:


LocalVariableTable Атрибут является необязательным атрибутом переменной длины для Code (§4.7.3) атрибута. Оно может использоваться отладчиками для определения значения данной локальной переменной во время выполнения метода.


В LocalVariableTable хранятся имена и типы переменных в каждом слоте, поэтому их можно сопоставить с байт-кодом. Именно так отладчики могут выполнять "Вычисление выражения".

Однако, как сказал Эриксон, нет способа получить доступ к этой таблице через обычное отражение. Если вы все еще полны решимости сделать это, я верю, что архитектура отладчика платформы Java (JPDA) поможет (но я сам никогда ею не пользовался).

Ответ 4
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));
}
}

}
java reflection