아이템 65. 리플렉션보다는 인터페이스를 사용하라. - ksw6169/effective-java GitHub Wiki

리플렉션의 단점

리플렉션을 이용하면 컴파일 당시에 존재하지 않던 클래스도 이용할 수 있는데, 단점이 있다.


1. 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다.

  • 프로그램이 리플렉션을 써서 존재하지 않거나 접근할 수 없는 메소드를 호출하려 시도하면 런타임 오류가 발생한다.

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

  • 지루하며, 읽기도 어렵다.

3. 성능이 떨어진다.

  • 리플렉션을 통한 메소드 호출은 일반 메소드 호출보다 훨씬 느리다. (필자 로컬 PC 기준 대략 11배)

리플렉션은 아주 제한된 형태로만 사용해야 한다.

  • 코드 분석 도구나 DI 프레임워크처럼 리플렉션을 써야 하는 복잡한 애플리케이션이 몇 가지 있다. 하지만 이런 도구들마저 리플렉션을 점차 줄이고 있다. 단점이 명백하기 때문이다.
  • 리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다.
  • 컴파일타임에 이용할 수 없는 클래스를 사용해야만 하는 프로그램은 비록 컴파일타임이라도 적절한 인터페이스나 상위 클래스를 이용할 수는 있을 것이다. 다행히 이런 경우라면 리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스로 참조해 사용하자.
public static void main(String[] args) {
    // 클래스 이름을 Class 객체로 변환
    Class<? extends Set<String>> cl = null;
    try {
        cl = (Class<? extends Set<String>>) Class.forName(args[0]); // HashSet이나 TreeSet 등의 클래스를 가져올 수 있다.
    } 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.err.println(msg);
    System.exit(1);
}

코드가 보여주는 리플렉션의 단점

1. 런타임에 총 여섯 가지나 되는 예외를 던진다.

  • 만약 인스턴스를 리플렉션 없이 생성했다면 컴파일 타임에 잡아낼 수 있었을 예외들이다.
  • 참고로 모든 리플렉션 예외의 상위 클래스인 ReflectiveOperationException (자바 7부터 지원)을 잡도록 하여 코드를 줄일 수도 있다.

2. 코드를 많이 작성해야 한다.

  • 클래스 이름만으로 인스턴스를 생성하기 까지 25줄이나 코드가 필요하다.
  • 리플렉션이 아니라면 생성자 호출 한 줄로 끝났을 일이다.

리플렉션을 사용하기 적절한 상황

  • 드물지만 리플렉션은 런타임에 존재하지 않을 수도 있는 다른 클래스, 메소드, 필드와의 의존성을 관리할 때 적합하다.
  • 이 기법은 버전이 여러 개 존재하는 외부 패키지를 다룰 때 유용하다. 가동할 수 있는 최소한의 환경, 즉 주로 가장 오래된 버전만을 지원하도록 컴파일한 후, 이후 버전의 클래스와 메소드 등은 리플렉션으로 접근하는 방식이다.

핵심 정리

  • 리플렉션은 복잡한 특수 시스템을 개발할 때 필요한 강력한 기능이지만, 단점도 많다.
  • 컴파일 타임에는 알 수 없는 클래스를 사용하는 프로그램을 작성한다면 리플렉션을 사용해야 한다.
  • 단, 되도록 객체 생성에만 사용하고, 생성한 객체를 이용할 때는 적절한 인터페이스나 컴파일타임에 알 수 있는 상위 클래스로 형변환해 사용해야 한다.

참고 자료

  • Effective Java 3/E
⚠️ **GitHub.com Fallback** ⚠️