Java 정리

리플렉션

으엉어엉 2024. 12. 27. 09:03
728x90

클래스가 제공하는 다양한 정보를 동적으로 분석하고 사용하는 기능을 리플렉션(Reflection) 이라 한다. 리플렉션을 통해 프로그램 실행 중에 클래스, 메서드, 필드 등에 대한 정보를 얻거나, 새로운 객체를 생성하고 메서드를 호출하며, 필드의 값을 읽고 쓸 수 있다. 리플렉션을 통해 얻을 수 있는 정보는 다음과 같다.

  • 클래스의 메타데이터 : 클래스 이름, 접근 제어자, 부모 클래스, 구현된 인터페이스 등.
  • 필드 정보 : 필드의 이름, 타입, 접근 제어자를 확인하고, 해당 필드의 값을 읽거나 수정할 수 있다.
  • 메서드 정보 : 메서드 이름, 반환 타입, 매개변수 정보를 확인하고, 실행 중에 동적으로 메서드를 호출할 수 있다.
  • 생성자 정보 : 생성자의 매개변수 타입과 개수를 확인하고, 동적으로 객체를 생성할 수 있다.
public class BasicData {
    public String publicField;
    private int privateField;
    public BasicData() {
        System.out.println("BasicData.BasicData");
    }
    private BasicData(String data) {
        System.out.println("BasicData.BasicData: " + data);
    }
    public void call() {
        System.out.println("BasicData.call");
    }
    public String hello(String str) {
        System.out.println("BasicData.hello");
        return str + " hello";
    }
    private void privateMethod() {
        System.out.println("BasicData.privateMethod");
    }
    void defaultMethod() {
        System.out.println("BasicData.defaultMethod");
    }
    protected void protectedMethod() {
        System.out.println("BasicData.protectedMethod");
    }
}
public static void main(String[] args) throws ClassNotFoundException {
    Class<BasicData> basicData = BasicData.class;
    System.out.println("basicData.getName() = " + basicData.getName());
    System.out.println("basicData.getSimpleName() = " +
            basicData.getSimpleName());
    System.out.println("basicData.getPackage() = " +
            basicData.getPackage());
    System.out.println("basicData.getSuperclass() = " +
            basicData.getSuperclass());
    System.out.println("basicData.getInterfaces() = " +
            Arrays.toString(basicData.getInterfaces()));
    System.out.println("basicData.isInterface() = " +
            basicData.isInterface());
    System.out.println("basicData.isEnum() = " + basicData.isEnum());
    System.out.println("basicData.isAnnotation() = " +
            basicData.isAnnotation());
    int modifiers = basicData.getModifiers();
    System.out.println("basicData.getModifiers() = " + modifiers);
    System.out.println("isPublic = " + Modifier.isPublic(modifiers));
    System.out.println("Modifier.toString() = " +
            Modifier.toString(modifiers));
}

위와 같은 코드를 통해 클래스이름, 패키지, 부모클래스 등등 다양한 정보를 가져올 수 있다.

 

public static void main(String[] args) {
    Class<BasicData> helloClass = BasicData.class;
    System.out.println("====== methods() =====");
    Method[] methods = helloClass.getMethods();
    for (Method method : methods) {
        System.out.println("method = " + method);
    }
    System.out.println("====== declaredMethods() =====");
    Method[] declaredMethods = helloClass.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println("declaredMethod = " + method);
    }
}
public static void main(String[] args) {
    Class<BasicData> helloClass = BasicData.class;
    System.out.println("====== methods() =====");
    Method[] methods = helloClass.getMethods();
    for (Method method : methods) {
        System.out.println("method = " + method);
    }
    System.out.println("====== declaredMethods() =====");
    Method[] declaredMethods = helloClass.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println("declaredMethod = " + method);
    }
}

일반 매서드 들과 동적 매서드들 또한 호출할 수 있다.

public static void main(String[] args) throws Exception {
    User user = new User("id1", "userA", 20);
    System.out.println("기존 이름 = " + user.getName());

    Class<? extends User> aClass = user.getClass();
    Field nameField = aClass.getDeclaredField("name");

    // private 필드에 접근 허용, private 메서드도 이렇게 호출 가능
    nameField.setAccessible(true);
    nameField.set(user, "userB");
    System.out.println("변경된 이름 = " + user.getName());
}

을 통해 변경도 가능하다..

 

리플렉션과 주의사항

리플렉션을 활용하면 private 접근 제어자에도 직접 접근해서 값을 변경할 수 있다. 하지만 이는 객체 지향 프로그래밍의 원칙을 위반하는 행위로 간주될 수 있다.private 접근 제어자는 클래스 내부에서만 데이터를 보호하고, 외부에 서의 직접적인 접근을 방지하기 위해 사용된다. 리플렉션을 통해 이러한 접근 제한을 무시하는 것은 캡슐화 및 유지보수 성에 악영향을 미칠 수 있다. 예를 들어, 클래스의 내부 구조나 구현 세부 사항이 변경될 경우 리플렉션을 사용한 코드는 쉽게 깨질 수 있으며, 이는 예상치 못한 버그를 초래할 수 있다. 따라서 리플렉션을 사용할 때는 반드시 신중하게 접근해야 하며, 가능한 경우 접근 메서드(예: getter, setter)를 사용 하는 것이 바람직하다. 리플렉션은 주로 테스트나 라이브러리 개발 같은 특별한 상황에서 유용하게 사용되지만, 일반적 인 애플리케이션 코드에서는 권장되지 않는다. 이를 무분별하게 사용하면 코드의 가독성과 안전성을 크게 저하시킬 수 있다.

 

728x90

'Java 정리' 카테고리의 다른 글

채팅 프로그램  (0) 2024.12.25
Logger  (0) 2024.11.22
코테 기술 정리  (0) 2024.11.13