Developer.

Reflect 패키지

📂 목차


📚 본문

Reflect 패키지

자바가 지원하는 문법에 대한 정보들을 제공하고 조작할 수 있는 패키지이며, 클래스와 객체에 대한 반사적 정보를 얻기 위한 클래스와 인터페이스를 제공하는 패키지이다. 여기서 일어나는 대부분은 Checked Exception 이고, 그만큼 컴파일러 단에서 예외처리를 잘 해주어야 한다.

자주 사용되는 것들만 보자.

Class

  • Class<T>: 클래스의 메타데이터를 담으며, Class.forName("패키지.클래스명") 또는 obj.getClass() 로 얻을 수 있다.
  • AccessibleObject - Field, Method 및 Constructor 객체의 기본 클래스
  • Field: 클래스의 멤버 변수 정보를 다룬다. 클래스에서 getDeclaredFields(), getField("name") 메서드를 통해 Field[] 를 얻을 수 있다.
  • Method: 클래스의 메서드 정보를 다루며, 클래스에서 getDeclaredMethods()로 배열을 가져올 수 있고, invoke() 로 실행을 할 수 있다.
  • Constructor<T>: 클래스의 생성자 정보
  • Modifier: public, private, static 등의 접근 제어자/수정자 정보를 확인한다.
  • Array: 리플렉션 기반의 배열 생성과 조작을 담당한다.
  • Parameter - 메서드 매개변수에 대한 정보

Interface

  • Type - Java 프로그래밍 언어의 모든 유형에 대한 공통 부모 인터페이스
  • WhildCardType - ? 를 의미
  • GenericDeclaration - 타입형을 지칭하는 공통 인터페이스

Reflect 를 활용한 클래스 정보 조회

Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println("클래스 이름: " + clazz.getName());

이때 ClassNotFoundException 이 일어날 수 있다.

Reflect 를 활용한 필드 접근 및 조작

import java.lang.reflect.*;


class Exam {
    private static int size = 100;

    public int getSize() {
        return size;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Exam");
        Object examInstance = clazz.getDeclaredConstructor().newInstance();

        System.out.println("클래스 이름: " + clazz.getName());

        Field field = clazz.getDeclaredField("size");
        field.setAccessible(true);  // private 접근 허용

        field.set(examInstance, 1);

        Exam exam = new Exam();
        System.out.println(exam.getSize());
    }
}

굳이 private 을 위와 같이 접근해서 쓰지는 말자.

Reflect 를 활용한 인스턴스 생성

Class clazz = Class.forName("클래스풀네임");

// Java 9 이후 권장 방법
Object obj = clazz.getDeclaredConstructor().newInstance();
// 또는 매개변수가 있는 생성자의 경우
Object obj2 = clazz.getDeclaredConstructor(String.class, int.class).newInstance("값", 10);

리플렉트를 사용해야 할 경우

주로 정적(compile-time)으로 타입을 확정하기 어려운 상황에서 동적으로 클래스/메서드/필드에 접근해야 할 때 활용되고, 의존성 주입, 테스트 코드에서 JUnit 등의 @Test 에서도 내부적으로 이를 사용한다. 또한 어노테이션, 동적 프록시 생성 등에서도 사용하게 된다.

물론 reflect 가 캡슐화를 깨뜨리는 API를 제공하고, 직접 호출보다 성능이 더 느리지만, 코딩 시점에서 정해지는 정적 타입 언어에 동적 성격을 부여할 수 있기 때문에 더 이상 자바로 처리하기 힘든 타입 바인딩에 대해 해결할 수 있는 중요한 수단이 되겠다.