Programing

Java 반사:변수 이름을 얻는 방법?

c10106 2022. 5. 17. 22:33
반응형

Java 반사:변수 이름을 얻는 방법?

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에서 함수에 전달된 변수의 이름을 찾을있는 방법이 있는가?다른 질문(허용된 답안 포함)은 변수의 값 검사에 더 집중되는 반면, 로컬 변수의 이름을 결정하기 위해 반사를 사용할 수 있는지에 대한 질문은 더 순수하게 묻는다는 점에서.

자바 8을 기준으로 반성을 통해 일부 로컬 변수 이름 정보를 이용할 수 있다.아래 "업데이트" 섹션을 참조하십시오.

완전한 정보는 종종 클래스 파일에 저장된다.컴파일 시간 최적화는 그것을 제거하고 공간을 절약하는 것이다(그리고 약간의 모호함을 제공한다).그러나 그것이 존재하는 경우, 각 방법에는 로컬 변수의 유형과 이름, 그리고 해당 변수의 범위에 해당하는 지시 범위를 나열하는 로컬 변수 테이블 속성이 있다.

아마도 ASM과 같은 바이트 코드 엔지니어링 라이브러리는 당신이 런타임에 이 정보를 검사할 수 있게 해줄 것이다.내가 이 정보가 필요하다고 생각할 수 있는 유일한 합리적인 장소는 개발 도구에 있으며, 따라서 바이트 코드 공학은 다른 목적에도 유용할 것 같다.


업데이트: 이에 대한 제한된 지원이 Java 8에 추가됨.매개변수(로컬 변수의 특수 클래스) 이름은 이제 반사를 통해 사용할 수 있다.다른 목적들 중에서, 이것은 대체하는데 도움이 될 수 있다.@ParameterNameAnnotations 의존성 주입 용기에 의해 사용하였습니다.종속성 주입 컨테이너가사용하는 주석.

그것은 전혀 가능하지 않다.변수 이름은 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아니면 동일한 추천을 받아 왔습니다 다른 들판이 있다.또는 동일한 참조를 가진 다른 필드가 있다.

질문이 명확해진 후 이제 EDIT 불필요

(편집: 이전 답변개를 삭제했는데, 하나는 편집 전에 있었던 질문에 대답하기 위한 것이고, 하나는 절대적으로 틀리지는 않더라도 최소한 그것에 근접하기 위한 것이다.)

에 대한 디버그 정보를 사용하여 컴파일하는 경우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 사양에 따르면 다음과 같은 이점을 얻을 수 있으며,

§4.7.9 속성:

LocalVariableTable속성은 의 선택적 가변 길이 속성이다.Code(제4.7.3항) 속성.디버거가 메서드를 실행하는 동안 지정된 로컬 변수의 값을 결정하는 데 사용할 수 있다.

LocalVariableTable각 슬롯에 변수의 이름과 유형을 저장하므로 바이트 코드와 일치시킬 수 있다.디버거는 이렇게 '평가표현'을 할 수 있다.

그러나 에릭슨이 말했듯이, 정상적인 반성을 통해 이 테이블에 접근할 수 있는 방법은 없다.아직 이 일을 하기로 마음먹었다면, 나는 Java Platform Debugger Architecture(JPDA)가 도움이 될 것이라고 믿는다(하지만 나는 직접 사용해 본 적은 없다).

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

}

이렇게 하면 된다.

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

참조URL: https://stackoverflow.com/questions/744226/java-reflection-how-to-get-the-name-of-a-variable

반응형