JAVA

Java 클래스의 변수명 가져오기

Ambitions 2021. 11. 23. 12:27

오늘은 Java Class에서 변수명, 어노테이션의 정보를 가져오는 방법을 정리해보려고 한다.

해당내용은 내가 작업하면서 사용했던 내용은 아니지만, 동료 직원이 고민했던 문제를 같이 고민하면서 찾게 되었기에 정리해본다.

 

대략 아래와 같은 클래스가 있을떄(보통 모델이나 뷰오브젝트 클래스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Lombok을 이용하여 Getter, Setter를 생략
@Getter
@Setter
public class TestClass {
 
    private Long id;
    
    private Integer seq;
 
    private String name;
 
    private String address;
 
    private String type;
 
    private Double amount;
 
}
cs

위 클래스의 변수명을 가져오고 싶다면 Class.java의 메소드중 getField라는 키워드만 기억하면 된다.

getField는 Java 공식 API문서의 설명을 번역기로 돌리면 "Field이 Class 개체가 나타내는 클래스 또는 인터페이스의 지정된 공용 멤버 필드를 반영 하는 개체를 반환 합니다." 라고 해석되는데, 실제로 Class.java에 선언된 메서드를 보면 다음과 같이 작성되어 있다.

1
2
3
4
5
6
7
8
9
@CallerSensitive
public Field getField(String name) throws NoSuchFieldException, SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    Field field = getField0(name);
    if (field == null) {
        throw new NoSuchFieldException(name);
    }
    return field;
}
cs

대략 checkMemberAccess, getField0(name)같은 메서드를 호출하고 있긴하지만, 이 부분까지 설명하려면 글이 길어질 것 같아, 간단하게 요약하면 문자열 name을 파라미터로 받아서 해당클래스 내에 파라미터 name을 가진 필드를 찾아서 리턴해주는 메서드이다.

 

따라서 getField메서드를 이용해서 코드를 실제로 작성해보면 다음과 같이 작성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//예외처리는 throws를 이용하여 처리해도 무관함.
public void printFieldName() {
 
    try {
        TestClass testClass = new TestClass();
        Field field = testClass.getClass().getField("memo");
        System.out.println(field.getName()); 
        // 위 코드의 원하는 결과는 문자열 memo지만 NoSuchFieldException 발생
        // 따라서 아래와 같이 작성해야함.
        TestClass testClass2 = new TestClass();
        Field field2 = testClass.getClass().getDeclaredField("memo");
        System.out.println(field2.getName() + field2.get(testClass)); 
    catch(Exception e) {
        System.out.println("Exception handling");
    }
}
cs

위 코드의 주석을 보면 알 수 있지만 getField를 그냥 사용 할 경우 NoSuchFieldException이 발생하게 된다.

NoSuchFieldException이 발생하는 이유는 getField 메서드는 클래스의 멤버변수가 공용(public)인 경우에만 멤버를 찾을 수 있는데, 위의 testClass의 경우 멤버변수들을 private으로 선언했기 때문에 예외가 발생하는 것이다. 

해결방안은 testClass의 멤버변수를 public type으로 바꾸거나 위의 코드 아래 부분처럼 getDeclaredField를 이용하면 정상적으로 멤버변수의 이름을 가져올 수 있다. 

getDeclareeField는 public으로 선언되있지 않은 멤버변수도 가져올 수 있다.

 

다음으로 클래스의 모든 개체의 이름을 가져오고 싶다면 다음과 같이 작성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public void printFieldName() {
 
    try {
        TestClass testClass = new TestClass();
        Field[] fields = testClass.getDeclaredFields(); 
        for(Field field : fields) {
            System.out.println(field.getName());
        }
 
    catch(Exception e) {
        System.out.println("Exception handling");
    }
}
cs

위와 같이 작성하면 testClass의 모든 개체의 변수명이 나오게 된다.

외에 Class의 get라는 메서드를 이용해서 해당 필드의 값을 가져올 수 있는데, 이 경우에도 접근제어자가 public이 아닌 경우(public인 경우에는 가져올 수 있다) IllegalAccessException이 발생하게 된다. 이 경우 필드명.setAccessible(true)를 이용하면 값(value)를 가져올 수 있는데 아래와 같이 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void printFieldName() {
 
    try {
        TestClass testClass = new TestClass();
        Field[] fields = testClass.getDeclaredFields(); 
        for(Field field : fields) {
            field.setAccessible(true); // 멤버변수의 값에 접근할 수 있게 해줌
            System.out.println(field.getName());
            System.out.println(field.get(field)); // 값 출력
        }
 
    catch(Exception e) {
        System.out.println("Exception handling");
    }
}
cs

 

이외에 Class의 메서드중 어노테이션의 타입, 값, 이름을 가져오는 메서들도 존재하는데, 실제로 사용 할일은 많지 않을 것 같다.

Java의 리플랙션, JVM, Class.java에 대해서 알아볼 수 있었던건 좋았던 것 같다.