#53 리플렉션보다는 인터페이스를 사용하자
리플렉션(Reflection)은 메모리에 로드된 클래스들의 관한 정보를 프로그램에서 이용할 수 있게 해줍니다.
1. 클래스가 메모리에 최초로 로드(.class 파일을 읽어 들일 때)
2. 그 클래스를 나타내는 Class라는 이름의 클래스 인스턴스를 생성
3. 그 클래스의 타입 정보와 같은 시스템 정보(메타 정보)를 런타임 시에 관리
리플렉션의 장단점과 좀 더 효율적으로 이용하기 위한 방법들에 대해 알아보겠습니다.
장점 #01. 생성자, 메서드, 필드를 재귀적으로 조작
주어진 클래스 인스턴스를 통해 Constructor, Method, Field 인스턴스를 얻을 수 있습니다.
그리고 이런 인스턴스들을 통해 해당 클래스의 멤버 이름, 필드 타입, 메서드 시그니처 등 프로그램 정보들을 얻어 사용할 수 있습니다.
게다가 이런 인스턴스들의 메서드를 호출하여 관련 클래스의 인스턴스를 만들고, 메서드를 호출하고, 필드를 이용할 수 있습니다.
단점 #01. 컴파일 시점에 가능한 타입 확인이 불가능
컴파일 시에 타입 확인이나 예외 검사를 할 수 없습니다.
따라서 만약 존재하지 않거나 접근 불가능한 메서드를 호출하려 하면 런타임 시에 실패하게 됩니다.
단점 #02. 재귀적인 접근을 필요로 하는 코드는 가독성이 떨어짐
이용하려는 메서드나 필드 명을 직접 입력해야 하기 때문에 작성이 귀찮고 가독성도 떨어집니다.
단점 #03. 처리 성능이 늦음
재귀적인 메서드 호출은 일반 메서드 호출보다 실행 속도가 많이 느립니다.
리플렉션은 장점과 더불어 몇 가지 단점이 존재합니다.
따라서 꼭 필요할 때만 써야 하는데, 클래스 브라우저/객체 조사기/코드 분석 도구 등과 같이 복잡한 애플리케이션에 쓰입니다.
애초에 리플렉션은 컴포넌트 기반의 애플리케이션 개발 도구용으로 설계되었습니다.
일반적으로 그런 도구 소프트웨어들은 필요시 클래스를 로드하고, 이 클래스의 메서드나 생성자가 어떤 것들인지 찾기 위해 리플렉션을 이용합니다.
즉, 일반적으로 런타임 시가 아닌 설계 시에만 이용해야 합니다.
효율적으로 이용하기 위해서는 장점은 살리고 단점들을 줄여야 합니다.
리플렉션을 지극히 제한된 형태로만 이용하면 효율적으로 이용할 수 있습니다.
리플렉션을 이용해 해당 클래스의 인스턴스 생성하고, 이를 인터페이스나 슈퍼 클래스를 통해 보통 때처럼 이용할 수 있습니다.
리플렉션 : 인스턴스 생성
인터페이스 or 슈퍼 클래스 : 메서드 호출이나 필드 이용
아래 코드는 리플렉션을 통해 Set<String> 인스턴스를 생성하고 있습니다.
1. main 메서드의 명령행 인자 중, 첫 번째 인자(java.util.TreeSet)를 받아 Class로 만듭니다.
cl = Class.forName(args[0]);
2. 클래스인 TreeSet이 아닌 Set 인터페이스 인스턴스를 생성합니다.
s = (Set<String>) cl.newInstance();
3. 명령행 인자 중, 두 번째 인자부터 Set 인터페이스에 추가한 후, 출력합니다.
s.addAll(Arrays.asList(args).subList(1, args.length));
System.out.println(s);
위 코드에서 리플렉션을 이용한 부분은 인스턴스 생성뿐입니다.
나머지 메서드 이용(addAll)은 Set 인터페이스를 이용했기 때문에, 모두 리플렉션을 이용했을 때보다 비용이 줄어듭니다.
리플렉션은 특정의 복잡한 시스템 프로그래밍에 필요하지만 단점도 많습니다.
따라서 컴파일 시점에 알 수 없는 클래스들과 함께 동작해야 한다면, 객체 생성을 리플렉션을 통해 할 수 있습니다.
그리고 이 객체를 이용할 때는 컴파일 시점에 알 수 있는 인터페이스나 슈퍼 클래스를 이용해야 합니다.