5월 28일-Reflection

Yullgiii·2024년 5월 30일
0
post-thumbnail

Reflection in Java

Reflection은 자바에서 이미 로딩이 완료된 클래스에서 또는 다른 클래스를 동적으로 로딩하여 구체적인 타입을 알지 못하더라도 생성자, 멤버 필드, 그리고 멤버 메소드를 사용할 수 있는 기법이다. 객체를 통해서 클래스의 패키지 정보, 접근 지정자, 부모 클래스, 어노테이션 등을 얻을 수 있다. 즉, 핵심은 컴파일 타임이 아니라 런타임에 동적으로 특정 클래스의 정보를 객체화하여 분석 및 추출해낼 수 있는 프로그래밍 기법이다.

Reflection을 사용하는 이유는?

  1. 동적 클래스 로딩: 실행 시간(Runtime)에 다른 클래스를 동적으로 로딩하여 접근할 필요가 있을 때 유용하다.
  2. 클래스 및 멤버 정보 추출: 클래스와 멤버 필드 그리고 메소드 등에 관한 정보를 얻어야 할 때 사용된다.
  3. 유연한 코드 작성: 리플렉션 없이도 완성도 높은 코드를 구현할 수 있지만, 사용한다면 조금 더 유연한 코드를 만들 수 있다. 예를 들어, 라이브러리나 프레임워크에서 플러그인을 로딩하거나, 테스트 프레임워크에서 테스트 메소드를 자동으로 실행할 때 주로 사용된다.

Reflection의 사용 예제

클래스 정보 가져오기

public class MyClass {
    private String field;

    public MyClass() {}

    public void setField(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }
}

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 클래스 로딩
            Class<?> clazz = Class.forName("MyClass");

            // 클래스 이름 출력
            System.out.println("Class Name: " + clazz.getName());

            // 생성자 정보 출력
            Constructor<?>[] constructors = clazz.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("Constructor: " + constructor.getName());
            }

            // 필드 정보 출력
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName());
            }

            // 메소드 정보 출력
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

필드에 값 설정하기

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("MyClass");
            Object instance = clazz.newInstance();

            // 필드에 접근하여 값 설정
            Field field = clazz.getDeclaredField("field");
            field.setAccessible(true);
            field.set(instance, "Hello, Reflection!");

            // 메소드를 통해 값 가져오기
            Method getFieldMethod = clazz.getMethod("getField");
            String fieldValue = (String) getFieldMethod.invoke(instance);
            System.out.println("Field Value: " + fieldValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

주의할 점

  1. Private 멤버 접근: 외부에 공개되지 않는 private 멤버도 Field.setAccessible(true) 메소드를 통해 접근과 조작이 가능하기 때문에 주의해서 사용해야 한다. 이는 보안 및 캡슐화의 원칙을 위반할 수 있다.
  2. 성능 저하: Reflection에는 동적으로 해석되는 유형이 포함되므로 특정 JVM 최적화를 수행할 수 없다. 따라서 Reflection 작업이 비 Reflection 작업보다 성능이 떨어지며, 성능에 민감한 애플리케이션에서 자주 호출되는 코드엔 사용하지 않아야 한다.
  3. 런타임 오류: 컴파일 타임에 체크되지 않는 런타임 오류가 발생할 가능성이 높아진다. 잘못된 클래스 이름, 필드 이름, 메소드 이름 등을 사용할 경우 ClassNotFoundException, NoSuchFieldException, NoSuchMethodException 등이 발생할 수 있다.

So...

Reflection은 강력한 기능을 제공하지만, 잘못 사용하면 성능 저하와 보안 문제를 일으킬 수 있다. 따라서 Reflection을 사용할 때는 필요한 경우에만 신중하게 사용해야 한다. 특히, 성능이 중요한 부분이나 보안에 민감한 부분에서는 가능한 사용을 피하는 것이 좋다. 하지만 적절히 사용한다면 매우 유연하고 강력한 코드를 작성할 수 있는 도구가 될 수 있다.

profile
개발이란 무엇인가..를 공부하는 거북이의 성장일기 🐢

0개의 댓글