Java反射:如何获得变量的名称?

yk9xbfzb  于 2023-01-11  发布在  Java
关注(0)|答案(8)|浏览(303)

使用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(***); 
    }
}
ut6juiuv

ut6juiuv1#

从Java 8开始,一些局部变量名信息可以通过反射获得。请参见下面的“更新”部分。
完整的信息通常存储在类文件中。一个编译时优化是将其删除,以节省空间(并提供一些混淆)。然而,当它存在时,每个方法都有一个局部变量表属性,列出局部变量的类型和名称,以及它们在作用域中的指令范围。
也许像ASM这样的字节码工程库可以让你在运行时检查这些信息,我能想到的需要这些信息的唯一合理的地方是在开发工具中,所以字节码工程也可能对其他目的有用。

  • 更新:* 对added to Java 8.参数(一个特殊的局部变量类)名称的支持有限,现在可以通过反射获得。除了其他目的之外,这可以帮助替换依赖注入容器使用的@ParameterName注解。
r6l8ljro

r6l8ljro2#

这是完全不可能的。变量名在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或其他字段具有相同的引用,则会出现问题。

由于问题已澄清,现在无需编辑

ctzwtxfj

ctzwtxfj3#

(* 编辑:删除之前的两个答案,一个是在编辑之前回答的问题,另一个是,如果不是绝对错误,至少接近它。*)
如果使用(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 LocalVariableTable属性:
LocalVariableTable属性是Code(§4.7.3)属性的一个可选的变长属性,它可以被调试器用来在方法执行期间确定给定局部变量的值。
LocalVariableTable在每个槽中存储变量的名称和类型,因此可以将它们与字节码匹配。这就是调试器如何执行“Evaluate expression”的方法。
正如erickson所说,虽然没有办法通过正常的反射来访问这个表,但是如果你仍然决心要这么做,我相信Java Platform Debugger Architecture (JPDA)会有所帮助(但是我自己从来没有用过)。

trnvg8h3

trnvg8h34#

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

}
7uhlpewt

7uhlpewt5#

你可以这样做:

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

o2rvlv0m6#

您需要做的就是创建一个字段数组,然后将其设置为您想要的类,如下所示。

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
klsxnrf1

klsxnrf17#

更新@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));
        }
    }
}
6tdlim6h

6tdlim6h8#

请参见以下示例:

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

相关问题