Можно ли получить имя локальной переменной с помощью Java Reflection? Например, если у меня есть это:
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 способ найти имя переменной, которая была передана функции? в том, что он более чисто задает вопрос о том, можно ли использовать отражение для определения имени локальной переменной, тогда как другой вопрос (включая принятый ответ) больше ориентирован на проверку значений переменных.
java
reflection
Дэвид Коэлле
источник
источник
Ответы:
Начиная с Java 8, некоторая информация об именах локальных переменных доступна через отражение. См. Раздел «Обновление» ниже.
Полная информация часто хранится в файлах классов. Одна из оптимизаций во время компиляции - удалить его, сэкономив место (и обеспечив некоторую запутанность). Однако, когда он присутствует, каждый метод имеет атрибут таблицы локальных переменных, в котором перечислены тип и имя локальных переменных, а также диапазон инструкций, в которых они находятся.
Возможно, инженерная библиотека байт-кода, такая как ASM , позволила бы вам проверять эту информацию во время выполнения. Единственное разумное место, где мне может понадобиться эта информация, - это инструмент разработки, поэтому разработка байтового кода, вероятно, будет полезна и для других целей.
Обновление: ограниченная поддержка этого была добавлена в Java 8. Имена параметров (специальный класс локальных переменных) теперь доступны через отражение. Помимо прочего, это может помочь заменить
@ParameterName
аннотации, используемые контейнерами внедрения зависимостей.источник
Это вообще невозможно. Имена переменных не передаются в 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
или есть другие поля, на которые есть такие же ссылки.ИЗМЕНИТЬ теперь ненужно, так как вопрос прояснился
источник
( Изменить: два предыдущих ответа удалены, один для ответа на вопрос, как он стоял до редактирования, и один, если не совсем неверен, по крайней мере, близок к нему. )
Если вы компилируете с отладочной информацией на (
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
хранит имена и типы переменных в каждом временном интервале, так что можно сопоставить их с байткод. Вот как отладчики могут выполнять «Оценить выражение».Однако, как сказал Эриксон, нет возможности получить доступ к этой таблице через обычное отражение. Если вы по-прежнему полны решимости сделать это, я считаю, что архитектура отладчика платформы Java (JPDA) поможет (но я никогда не использовал ее сам).
источник
javac
для облегчения отладки в класс помещает таблицу локальных переменных для каждого метода. Используйте-l
опцию дляjavap
просмотра таблицы локальных переменных.javac -g:vars
чтобы получить это. (Я пытался отредактировать этот ответ в течение последних трех часов, но, как я уже сказал, у моего сетевого подключения возникают проблемы, что затрудняет исследование.)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)); } } }
источник
getDeclaredFields()
можно использовать, если вам нужны имена частных полейСделать можно так:
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()); }
источник
Все, что вам нужно сделать, это создать массив полей, а затем установить для него нужный класс, как показано ниже.
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
источник
обновить ответ @Marcel Jackwerth для общего.
и работать только с атрибутом класса, а не с переменной метода.
/** * get variable name as string * only work with class attributes * not work with method variable * * @param headClass variable name space * @param vars object variable * @throws IllegalAccessException */ public static void printFieldNames(Object headClass, Object... vars) throws IllegalAccessException { List<Object> fooList = Arrays.asList(vars); for (Field field : headClass.getClass().getFields()) { if (fooList.contains(field.get(headClass))) { System.out.println(field.getGenericType() + " " + field.getName() + " = " + field.get(headClass)); } } }
источник
см. этот пример:
PersonneTest pt=new PersonneTest(); System.out.println(pt.getClass().getDeclaredFields().length); Field[]x=pt.getClass().getDeclaredFields(); System.out.println(x[1].getName());
источник