65. 리플렉션보다는 인터페이스를 사용하라
주제: 리플렉션보다는 인터페이스를 사용하라
(핵심)
java.lang.reflect패키지를 통해 런타임에 클래스 정보를 얻고 조작할 수 있지만, 단점이 많아 매우 제한적으로 사용해야 하며, 가능하다면 인터페이스 기반 접근을 우선해야 한다.
리플렉션이란?
프로그램 실행 중(런타임)에 임의의 클래스에 접근 가능.
Class,Constructor,Method,Field인스턴스.가능한 작업
클래스/멤버 정보 조회
실제 멤버 조작:
인스턴스 생성 (
Constructor.newInstance()).메서드 호출 (
Method.invoke()).필드 값 접근/수정 (
Field.get(),Field.set()).
컴파일 시점에는 존재하지 않거나 알 수 없었던 클래스도 런타임에 로드하여 사용 가능.
리플렉션의 단점
컴파일타임 검사 이점 상실 (Loss of Compile-time Checking Benefits)
타입 안전성 X
잘못된 타입의 객체나 메서드 사용 시 런타임 오류 발생.
예외 검사 X
리플렉션 API는 확인된 예외(Checked Exception)를 제대로 검사하지 못함.
존재하지 않거나 접근 불가한 멤버 호출 시
RuntimeException또는 관련 리플렉션 예외 발생.
지저분하고 장황한 코드
단순한 생성/호출에도 많은 리플렉션 API 호출 및 광범위한
try-catch블록 필요. 가독성 저하.
성능 저하
일반적인 메서드 호출이나 필드 접근보다 '훨씬' 느림. 분석 및 해석 오버헤드 발생.
(참고) 실제 테스트 시 메서드 호출 속도 11배 차이.
권장 사용 패턴
아주 제한적 사용
핵심
생성은 리플렉션, 사용은 인터페이스/상위 클래스
컴파일 시점에는 구체 클래스를 알 수 없지만, 사용할 인터페이스나 상위 클래스는 아는 경우.
과정
리플렉션 API를 사용해 인스턴스를 생성.
Class.forName(),getDeclaredConstructor(),newInstance()
생성된 인스턴스를 컴파일 시점에 아는 인터페이스나 상위 클래스 타입으로 형변환하여 변수에 할당.
이후 코드에서는 해당 인터페이스/상위 클래스에 정의된 메서드만 사용하여 객체 처리. (리플렉션 코드와 분리)
예시 분석 (Code 65-1: 명령줄 인수로 Set 구현체 동적 생성)
목표 : 프로그램 실행 시 주어진 클래스 이름으로
Set<String>인스턴스를 동적으로 생성하고 사용하는 것.예:
java.util.HashSet,java.util.LinkedHashSet
리플렉션 과정
Class.forName(args[0]): 문자열 이름으로Class객체 로드.cl.getDeclaredConstructor(): 기본 생성자(Constructor) 객체 얻기.cons.newInstance(): 생성자를 호출하여 실제Set인스턴스 생성.이 과정에서
ClassNotFoundException,NoSuchMethodException,IllegalAccessException,InstantiationException,InvocationTargetException,ClassCastException등 다양한 예외 처리 필요.
인터페이스 사용
Set<String> s = ...: 생성된 객체를Set인터페이스 타입 변수s로 받음.s.addAll(...),System.out.println(s): 이후s변수를 통해Set인터페이스의 표준 메서드만 사용.이 부분은 리플렉션과 무관하게 타입 안전하고 명확함
예시가 보여주는 단점
6가지 잠재적 런타임 예외 발생 가능성 (컴파일 시점 확인 불가).
단 한 줄(
new HashSet<>())이면 될 인스턴스 생성을 위해 25줄의 복잡한 코드 필요.
고급/특수 사용 사례 (Advanced/Special Use Case)
다중 버전 외부 라이브러리 지원 (Supporting Multi-version External Libraries)
호환성을 위해 가장 오래된 버전(최소 요구사항)의 라이브러리로 컴파일.
런타임에 더 최신 버전의 라이브러리가 존재할 경우, 해당 버전에만 있는 추가 클래스나 메서드를 리플렉션으로 감지하고 접근 시도.
(필수 조건) 해당 최신 기능이 런타임에 없을 경우를 반드시 대비해야 함
예: 대체 로직 실행, 기능 비활성화
최종 요약 (Key Takeaway)
"리플렉션은 특정 문제(컴파일 시점에 알 수 없는 클래스 사용 등) 해결에 강력하지만, 코드 가독성/타입 안전성/성능 저하라는 명확한 단점이 있다.
필요하다면 사용하되, 가급적 인스턴스 생성에만 국한하고, 생성된 객체는 반드시 인터페이스나 상위 클래스로 형변환하여 참조하고 사용함으로써 리플렉션의 단점을 코드 전체로 확산시키지 않도록 격리하라."
Last updated