Skip to content

Latest commit

 

History

History
157 lines (113 loc) · 7.14 KB

65_리플렉션보다는_인터페이스를_사용하라_김보배.md

File metadata and controls

157 lines (113 loc) · 7.14 KB

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

리플렉션 (Reflection)

1

리플렉션(java.lang.reflect)를 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다.

  • 클래스의 생성자, 메서드, 필드에 해당하는 constructor, method, field 인스턴스를 가져올 수 있다.
  • 클래스의 멤버 이름, 필드 타입, 메서드 시그니처 등을 가져올 수 있다.
  • 위의 것들을 활용해서 실제로 인스턴스를 생성하거나, 메서드를 호출하거나, 필드에 접근할 수도 있다.
  • 컴파일 당시에 존재하지 않던 클래스도 이용할 수 있다.


간단히 만들어 본 예제

KimDoubleB/JavaStartingFromBottom

  • Class.forName
  • getDeclaredConstructors()
  • getDeclaredMethods()
  • getDeclaredFields()
  • setAccessible(boolean)
  • newInstance(parameter)
  • invoke(class, parameter)

public, private 상관없이 다 접근이 가능하고 생성, 사용 및 변경까지 가능하다.



리플렉션 사용의 단점

  • 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다.
  • 리플렉션을 이용하면 코드가 지저분해지고 장황해진다.
    • 각종 오류 처리로 인해 로직을 보기 힘들며, 코드를 읽기가 힘들어진다.
  • 성능이 떨어진다.
    • 훨씬 느리고, 책에 의하면 int 반환 메서드의 경우 일반 메서드에 비해 11배나 느렸다고 한다.

⇒ 코드 분석 도구나 의존관계 주입 프레임워크 같이 리플렉션을 사용해야하는 경우가 있지만, 만약 리플렉션이 필요한지 확신할 수 없다면 아마 필요 없을 것이다. (사용하지 마라...) - 이런 도구들마저도 위 단점들 때문에 리플렉션 사용을 점차 줄이고 있다고 함.



그럼에도 사용해야 한다면 <이번 챕터 제목이 말하는 바>

리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다. (그래도 해결하지 못하는 단점들은 존재)

  • 인터페이스 및 상위클래스로 참조해 사용하자. 즉, 리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위클래스로 참조해 사용하자.
  • SomeInterface instance = InstanceMakerUsingReflection()


예제

아래 예제에서는 Set<String> 인터페이스를 사용한 예이다.

  • args를 통해 첫 번째 인수로 지정한 클래스가 무엇이냐에 따라 저장되는 순서(구조)가 달라진다.
    • 즉, 컴파일타임에는 어떤 클래스인지 알 수 없다.
  • HashSet을 지정하면 무작위 순서가 될 것이고, TreeSet을 지정하면 알파벳 순서가 될 것이다.
public static void main(String[] args) {
    Class<? extends Set<String>> cl = null;
    // 클래스 이름을 클래스 객체로 변환
    try {
        cl = (Class<? extends Set<String>>) Class.forName(args[0]);
    } catch (ClassNotFoundException e) {
        fatalError("클래스를 찾을 수 없습니다.");
    }

    // 생성자를 얻어오기
    Constructor<? extends Set<String>> cons = null;
    try{
        cons = cl.getDeclaredConstructor();
    } catch (NoSuchMethodException e) {
        fatalError("매개변수 없는 생성자를 찾을 수 없습니다.");
    }

    // 위에서 만든 클래스를 통해 집합 인스턴스 생성
    Set<String> s = null;
    try{
        s = cons.newInstance();
    } catch (IllegalAccessException e) {
        fatalError("생성자에 접근할 수 없습니다.");
    } catch (InstantiationException e) {
        fatalError("클래스를 인스터화 할 수 없습니다.");
    } catch (InvocationTargetException e) {
        fatalError("생성자가 예외를 던졌습니다." + e.getCause());
    } catch (ClassCastException e){
        fatalError("Set을 구현하지 않은 클래스 입니다.");
    }

    // 생성한 인스턴스 사용
    s.addAll(Arrays.asList(args).subList(1, args.length));
    System.out.println(s);
}

private static void fatalError(String msg){
    System.out.println(msg);
    System.exit(1);
}

2

위 예를 통해 리플렉션의 단점을 직접 확인할 수 있다.

  • 런타임 중에 총 여섯가지나 되는 예외를 던질 수 있다.
    • 이 예외들이 인스턴스를 리플렉션 없이 생성했다면 컴파일 타임에 잡아낼 수 있었을 예외들
  • 클래스 이름만으로 인스턴스를 생성해내기 위해 무려 25줄 이상의 코드를 작성했다.
    • 리플렉션이 아니라면 생성자를 호출하는 한 줄로 끝났을 일

그래도 위 과정으로 인스턴스를 만들어내면, 이후 사용하는 코드는 기존 인스턴스를 사용할 때와 똑같다.



그럼, Reflection API는 어디서 사용할까?

위에서 말했듯 코드 분석 도구, 의존관계 주입 프레임워크에서 많이 사용된다.

예를 들어


보통 일반? 개발자가 활용할 땐, 어노테이션과 리플렉션 조합으로 원하는 로직을 만드는 경우가 많은 것 같다.

Spring Annotation 과 Reflection 을 활용해서 Entity의 여러 필드 한번에 수정하기

Java Custom Annotations Example - Mkyong.com

Java Annotation과 Reflection이란?

Annotation과 Reflection을 이용한 챗봇 컨트롤러 만들기



References