JAVA/Effective Java

[아이템 65] 리플렉션보다는 인터페이스를 사용하라

꾸준함. 2024. 3. 19. 05:09

리플렉션 기능을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있으며 Class 객체가 주어지면 클래스 정보를 통해 다음과 같은 인스턴스를 가져올 수 있습니다.

  • 생성자
  • 메서드
  • 필드

 

1. 생성자

  • 생성자 시그니처를 가져올 수 있음
  • 생성자 인스턴스를 통해 객체를 생성할 수 있음

 

2. 메서드

  • 메서드 시그니처를 가져올 수 있음
  • 메서드 인스턴스를 통해 메서드를 실행시킬 수 있음

 

3. 필드

  • 필드 타입, 멤버 필드명 등을 가져올 수 있음

 

MyClass.java

 

 

 

ReflectionExample.java

 

 

리플렉션의 단점

앞서 코드에서 볼 수 있었다시피 리플렉션은 강력한 기능이지만 다음과 같은 단점들이 존재합니다.

  • 컴파일 시점 타입 검사가 주는 이점을 누릴 수 없음
  • 리플렉션을 이용하면 코드가 지저분해지고 장황해짐
  • 성능 저하

 

1. 컴파일 시점 타입 검사가 주는 이점을 누릴 수 없음

 

  • 예외 검사 및 컴파일 에러를 잡아 낼 방법이 없음
  • 프로그램이 리플렉션 기능을 써서 존재하지 않는 혹은 private 메서드를 호출하려고 하면 런타임 오류 발생

 

MyClass에 private 메서드를 추가하면 ReflectionExample 코드를 돌릴 때 IllegalArgumentException 발생

 

 

 

2. 리플렉션을 이용하면 코드가 지저분하고 장황해짐

 

  • 원래라면 instance.displayInfo()만 호출하면 되는데
  • displayInfo() 메서드를 호출하기 전 작성해야 할 코드가 너무 많음
  • 지루한 일이고 가독성 측면에서 좋지 않음

 

3. 성능 저하

 

  • 리플렉션을 통한 메서드 호출은 일반 메서드 호출보다 훨씬 느림
    • 제가 직접 돌려본 결과 별 차이 없었음...

 

리플렉션은 아주 제한된 형태로만 사용해야 단점을 회피할 수 있음

  • 컴파일 시점에 이용할 수 없는 클래스를 사용해야만 하는 프로그램은 비록 컴파일 타임이라도 적절한 인터페이스나 상위 클래스를 이용할 수는 있음
    • 프로그램이 실행 중에 동적으로 클래스를 로드하고 사용해야 하는 경우가 있음
    • 이런 경우에는 컴파일 시에는 해당 클래스에 대한 정보가 없기 때문에 정적인 방식으로는 접근할 수 없음
    • 하지만 리플렉션을 사용하면 실행 중에 해당 클래스를 로드하고 그에 따른 작업을 수행할 수 있음
    • 이때는 적절한 인터페이스나 상위 클래스를 이용하여 코드의 유연성을 유지할 수 있음
    • 리플렉션은 인스턴스 생성에만 이용하고 이렇게 만든 인터페이스나 상위 클래스로 참조해 사용하는 것을 권장

 

리플렉션을 사용했을 때 단점을 보여주는 코드

 

 

 

코드 부연 설명

  • 런타임에 총 6가지의 예외를 던질 수 있음
    • 리플렉션을 사용하지 않았더라면 컴파일 시점에 체크할 수 있는 예외
    • 모두 Checked Exception

 

  • 클래스명만으로 인스턴스를 생성하기 위해 무려 25줄이나 되는 코드를 작성
    • 리플렉션 사용하지 않았을 경우 생성자 1줄이면 끝

 

  • 리플렉션 예외를 각각 잡는 대신 자바 7 버전부터 도입한 상위 클래스인 ReflectiveOperationException을 잡으면 코드량을 줄일 수 있음
    • ReflectiveOperationExceptionClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException 등을 포함하는 예외 클래스

 

 

리플렉션은 언제 사용해야 할까?

  • 스프링 프레임워크는 많은 기능에서 리플렉션을 활용합니다..
    • 의존성 주입 (Dependency Injection): 스프링은 객체 간의 의존성을 관리하기 위해 리플렉션을 사용
    • Aspect-Oriented Programming (AOP): 스프링에서 AOP를 구현할 때 리플렉션을 사용
    • 프록시 (Proxy) 생성: 스프링은 AOP를 구현할 때 주로 프록시를 사용하며 프록시는 대상 객체를 감싸고 호출을 가로채는데, 이를 위해 리플렉션을 사용하여 프록시를 동적으로 생성
    • 데이터 바인딩: 스프링 MVC에서는 HTTP 요청 파라미터를 자바 객체의 필드에 바인딩할 때 리플렉션을 사용합니다. 즉, 요청 파라미터와 매핑된 객체를 생성하고, 객체의 필드에 값을 설정하기 위해 리플렉션을 사용
    • 컨테이너 초기화 및 구성: 스프링은 XML 또는 어노테이션 기반의 설정을 통해 애플리케이션 컨텍스트를 초기화하고 구성
      • 리플렉션을 사용하여 클래스를 로드하고, 필요한 설정을 적용하며, 빈을 등록

 

  • 컴파일 시점에는 알 수 없는 클래스를 사용하는 프로그램을 작성한다면 리플렉션을 사용해야 함
    • 단, 되도록 객체 생성에만 사용하고, 생성한 객체를 이용할 때는 적절한 인터페이스나 컴파일 시점에 알 수 있는 상위 클래스로 형변환해 사용하는 것을 권장

 

참고

이펙티브 자바

반응형