[Effective Java]리플렉션보다는 인터페이스를 사용하라

일반적인 프로그래밍 원칙, 아홉 번째 아이템

Posted by SungBeom on May 12, 2020 · 2 mins read

리플렉션

리플렉션 기능(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다. Class 객체가 주어지면 그 클래스의 생성자, 메소드, 필드에 해당하는 Constructor, Method, Field 인스턴스를 가져올 수 있고, 이어서 이 인스턴스들로는 그 클래스의 멤버 이름, 필드 타입, 메소드 시그니처 등을 가져올 수 있다.

나아가 Constructor, Method, Field 인스턴스를 이용해 각각에 연결된 실제 생성자, 메소드, 필드를 조작할 수도 있다. 이 인스턴스들을 통해 해당 클래스의 인스턴스를 생성하거나, 메소드를 호출하거나, 필드에 접근할 수 있다는 뜻이다. 예를 들어 Method.invoke는 어떤 클래스의 어떤 객체가 가진 어떤 메소드라도 호출할 수 있게 해준다(물론 일반적인 보안 제약사항은 준수해야 한다). 리플렉션을 이용하면 컴파일 당시에 존재하지 않던 클래스도 이용할 수 있다.

리플렉션의 단점

리플렉션에도 물론 단점이 있다.

컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 예외 검사도 마찬가지다. 프로그램이 리플렉션 기능을 써서 존재하지 않는 혹은 접근할 수 없는 메소드를 호출하려 시도하면 런타임 오류가 발생한다.

리플렉션을 이용하면 코드가 지저분하고 장황해진다. 지루한 일이고, 읽기도 어렵다.

성능이 떨어진다. 리플렉션을 통한 메소드 호출은 일반 메소드 호출보다 훨씬 느리다.

리플렉션의 올바른 사용법

리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다. 컴파일타임에 이용할 수 없는 클래스를 사용해야만 하는 프로그램은 비록 컴파일타임이라도 적절한 인터페이스나 상위 클래스를 이용할 수는 있을 것이다. 다행히 이런 경우라면 리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스로 참조해 사용하자.

드물긴 하지만, 리플렉션은 런타임에 존재하지 않을 수도 있는 다른 클래스, 메소드, 필드와의 의존성을 관리할 때 적합하다. 이 기법은 버전이 여러 개 존재하는 외부 패키지를 다룰 때 유용하다. 가동할 수 있는 최소한의 환경, 즉 주로 가장 오래된 버전만을 지원하도록 컴파일한 후, 이후 버전의 클래스와 메소드 등은 리플렉션으로 접근하는 방식이다. 이렇게 하려면 접근하려는 새로운 클래스나 메소드가 런타임에 존재하지 않을 수 있다는 사실을 반드시 감안해야 한다. 즉, 같은 목적을 이룰 수 있는 대체 수단을 이용하거나 기능을 줄여 동작하는 등의 적절한 조치를 취해야 한다.


핵심 정리

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