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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/744226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 18:57:49  来源:igfitidea点击:

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

javareflection

提问by David Koelle

Using Java Reflection, is it possible to get the name of a local variable? For example, if I have this:

使用 Java 反射,是否可以获取局部变量的名称?例如,如果我有这个:

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

is it possible to implement a method that can find the names of those variables, like so:

是否可以实现一种可以找到这些变量名称的方法,如下所示:

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

EDIT: This question is different from Is there a way in Java to find the name of the variable that was passed to a function?in that it more purely asks the question about whether one can use reflection to determine the name of a local variable, whereas the other question (including the accepted answer) is more focused on testing values of variables.

编辑:这个问题不同于Java 中有没有办法找到传递给函数的变量的名称?因为它更纯粹地询问是否可以使用反射来确定局部变量的名称,而另一个问题(包括已接受的答案)更侧重于测试变量的值。

采纳答案by erickson

As of Java 8, some local variable name information is available through reflection. See the "Update"section below.

从 Java 8 开始,一些局部变量名信息可以通过反射获得。请参阅下面的“更新”部分。

Complete information is often stored in class files. One compile-time optimization is to remove it, saving space (and providing some obsfuscation). However, when it is is present, each method has a local variable table attribute that lists the type and name of local variables, and the range of instructions where they are in scope.

完整的信息通常存储在类文件中。一种编译时优化是删除它,节省空间(并提供一些混淆)。然而,当它存在时,每个方法都有一个局部变量表属性,它列出了局部变量的类型和名称,以及它们在范围内的指令范围。

Perhaps a byte-code engineering library like ASMwould allow you to inspect this information at runtime. The only reasonable place I can think of for needing this information is in a development tool, and so byte-code engineering is likely to be useful for other purposes too.

也许像ASM这样的字节码工程库可以让您在运行时检查这些信息。我认为需要这些信息的唯一合理的地方是在开发工具中,因此字节码工程也可能用于其他目的。



Update:Limited support for this was added to Java 8.Parameter (a special class of local variable) names are now available via reflection. Among other purposes, this can help to replace @ParameterNameannotations used by dependency injection containers.

更新:Java 8添加了对此的有限支持参数(一种特殊的局部变量类)名称现在可通过反射获得。除其他用途外,这有助于替换@ParameterName依赖注入容器使用的注解。

回答by Marcel Hymanwerth

It is notpossible at all. Variable names aren't communicated within Java (and might also be removed due to compiler optimizations).

不是不可能的。变量名不在 Java 中进行通信(并且也可能由于编译器优化而被删除)。

EDIT (related to comments):

编辑(与评论有关):

If you step back from the idea of having to use it as function parameters, here's an alternative (which I wouldn't use - see below):

如果您放弃将其用作函数参数的想法,这里有一个替代方案(我不会使用 - 见下文):

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

There will be issues if a == b, a == r, or b == ror there are other fields which have the same references.

如果a == b, a == r, or b == r或存在其他具有相同引用的字段,则会出现问题。

EDIT now unnecessary since question got clarified

由于问题得到澄清,现在不需要编辑

回答by Michael Myers

(Edit: two previous answers removed, one for answering the question as it stood before edits and one for being, if not absolutely wrong, at least close to it.)

编辑:删除了两个先前的答案,一个用于回答编辑前的问题,另一个用于回答,如果不是绝对错误,至少接近它。

If you compile with debug information on (javac -g), the names of local variables are kept in the .class file. For example, take this simple class:

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

After compiling with javac -g:vars TestLocalVarNames.java, the names of local variables are now in the .class file. javap's -lflag ("Print line number and local variable tables") can show them.

用 编译后javac -g:vars TestLocalVarNames.java,局部变量的名称现在在 .class 文件中。javap-l标志(“打印行号和局部变量表”)可以显示它们。

javap -l -c TestLocalVarNamesshows:

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

The VM specexplains what we're seeing here:

虚拟机规范解释了我们现在看到的:

§4.7.9 The LocalVariableTableAttribute:

§4.7.9LocalVariableTable属性

The LocalVariableTableattribute is an optional variable-length attribute of a Code(§4.7.3) attribute. It may be used by debuggers to determine the value of a given local variable during the execution of a method.

LocalVariableTable属性是Code(第 4.7.3 节)属性的可选可变长度属性。调试器可以使用它来确定方法执行期间给定局部变量的值。

The LocalVariableTablestores the names and types of the variables in each slot, so it is possible to match them up with the bytecode. This is how debuggers can do "Evaluate expression".

LocalVariableTable变量的名称和类型存储在每个插槽中,因此可以将它们与字节码进行匹配。这就是调试器可以执行“评估表达式”的方式。

As erickson said, though, there's no way to access this table through normal reflection. If you're still determined to do this, I believe the Java Platform Debugger Architecture (JPDA)will help (but I've never used it myself).

但是,正如 erickson 所说,无法通过正常反射访问此表。如果您仍然决心这样做,我相信Java Platform Debugger Architecture (JPDA)会有所帮助(但我自己从未使用过)。

回答by Peeter

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

}

回答by error_null_pointer

All you need to do is make an array of fields and then set it to the class you want like shown below.

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

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

For example if you did

例如,如果你做了

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

you would get

你会得到

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

回答by h.meknassi

see this example :

看这个例子:

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

回答by tinker_fairy

You can do like this:

你可以这样做:

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

回答by Colin Wang

update @Marcel Hymanwerth's answer for general.

更新@Marcel Hymanwerth 的一般回答。

and only working with class attribute, not working with method variable.

并且只使用类属性,而不使用方法变量。

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