Java
리플렉션
- 클래스가 제공하는 다양한 정보(메타 데이터)를 런타임에 동적으로 분석하고 사용하는 기능
- e.g. 스프링 프레임워크가 내가 만든 클래스를 대신 생성해주는 경우
- 메타데이터 종류
- 클래스
- e.g. 클래스 이름, 접근 제어자, 부모 클래스, 구현한 인터페이스
- 필드
- e.g. 필드 이름, 타입, 접근 제어자
- 런타임에 동적으로 해당 필드 값을 읽거나 수정 가능
- 메서드
- e.g. 메서드 이름, 반환 타입, 매개변수 정보
- 런타임에 동적으로 메서드 조회 및 호출 가능
- 생성자
- e.g. 매개변수 타입 및 개수
- 런타임에 동적으로 생성자 조회 및 객체 생성 가능
- 주의점
- 리플렉션 코드는 특별한 상황에서 사용
- 공통 문제를 해결하는 유틸리티, 프레임워크, 라이브러리 개발
-
테스트 등
-
일반적인 애플리케이션은 권장 X
- 무분별한 리플렉션 사용은 코드의 가독성과 안정성이 크게 저하
- e.g.
-
private
직접 접근은 객체 지향 원칙을 위반 (캡슐화 및 유지보수성 저하)
- 클래스 내부 구조나 구현 세부사항이 변경되면 쉽게 깨지거나 버그를 초래
- 리플렉션은 문자를 활용하므로, 필드 및 메서드 이름 변경 시 컴파일러가 놓침
클래스 메타데이터
- 클래스의 메타데이터는
Class
클래스로 표현
-
Class
조회 방법
-
클래스에서 찾기
클래스명.class
- e.g.
Class<BasicData> basicDataClass1 = BasicData.class;
-
인스턴스에서 찾기
인스턴스.getClass()
- e.g.
BasicData basicInstance = new BasicData();
Class<? extends BasicData> basicDataClass2 = basicInstance.getClass();
-
문자로 찾기
Class.forName(패키지명문자열)
- e.g.
String className = "reflection.data.BasicData";
Class<?> basicDataClass3 = Class.forName(className);
- 기본 정보 탐색
- 클래스 이름
- 경로 포함 이름:
basicData.getName() //reflection.data.BasicData
- 클래스 이름:
basicData.getSimpleName() //BasicData
- 패키지
basicData.getPackage() //package reflection.data
- 부모 클래스
basicData.getSuperclass() //class java.lang.Object
- 구현한 인터페이스
basicData.getInterfaces() //[]
- 조건 판별
basicData.isInterface() //false
basicData.isEnum() //false
basicData.isAnnotation() //false
- 수정자 정보 (규칙있는 숫자로 리턴)
basicData.getModifiers() //1
- 참고: 수정자는 접근제어자와 비접근제어자(기타 수정자)로 분류
- 접근 제어자:
public
, protected
, default
( package-private
), private
- 비 접근 제어자:
static
, final
, abstract
, synchronized
, volatile
등
메서드 메타데이터
-
Method
클래스로 표현 (클래스 메타데이터를 통해 획득 가능)
- 메서드 메타데이터 조회
-
getMethod(메서드이름, 매개변수타입)
-
해당 클래스와 상위 클래스에서 상속된 모든 public 메서드 중 지정 메서드 조회
- e.g.
String methodName = "hello";
Method method = helloClass.getMethod(methodName, String.class);
-
getDeclaredMethod(메서드이름, 매개변수타입)
-
해당 클래스에서 선언된 모든 메서드 중 지정 메서드 조회
- e.g.
String methodName = "hello";
Method method = helloClass.getMethod(methodName, String.class);
-
getMethods()
-
해당 클래스와 상위 클래스에서 상속된 모든 public 메서드를 반환
- e.g.
Class<BasicData> helloClass = BasicData.class;
Method[] methods = helloClass.getMethods();
-
getDeclaredMethods()
-
해당 클래스에서 선언된 모든 메서드를 반환
- 접근 제어자에 관계 X, 상속된 메서드 포함 X
- e.g.
Class<BasicData> helloClass = BasicData.class;
Method[] declaredMethods = helloClass.getDeclaredMethods();
- 동적 메서드 호출
- 메서드 이름을 입력 받으면, 호출 대상 메서드를 동적으로 조회해 호출 가능
-
getMethod()
, getDeclaredMethod()
로 메서드 동적 조회
-
Method
객체의 invoke(인스턴스, 인자1, ...)
로 메서드 호출
- e.g.
Class<? extends BasicData> helloClass = helloInstance.getClass();
String methodName = "hello";
Method method = helloClass.getDeclaredMethod(methodName, String.class);
Object returnValue = method.invoke(helloInstance, "hi");
필드 메타데이터
-
Field
클래스로 표현 (클래스 메타데이터를 통해 획득 가능)
- 필드 조회
-
getField(필드이름)
-
해당 클래스와 상위 클래스에서 상속된 모든 public 필드 중 지정 필드 조회
- e.g.
Field nameField = aClass.getField("name");
-
getDeclaredField(필드이름)
-
해당 클래스에서 선언된 모든 필드 중 지정 필드 조회
- e.g.
Field nameField = aClass.getDeclaredField("name");
-
getFields()
-
해당 클래스와 상위 클래스에서 상속된 모든 public 필드를 반환
- e.g.
Class<BasicData> helloClass = BasicData.class;
Field[] fields = helloClass.getFields();
-
getDeclaredFields()
-
해당 클래스에서 선언된 모든 필드를 반환
- 접근 제어자에 관계 X, 상속된 필드 포함 X
- e.g.
Class<BasicData> helloClass = BasicData.class;
Field[] declaredFields = helloClass.getDeclaredFields();
- 필드 값 변경
-
setAccessible(true)
-
private
필드에 직접 접근해 변경할 수 있는 기능
- e.g.
nameField.setAccessible(true)
- 참고:
private
메서드, 생성자에서도 사용 가능 (Method
, Constructor
)
-
set(인스턴스, 변경값)
- 필드 값 변경 메서드
- e.g.
nameField.set(user, "userB")
생성자 메타데이터
-
Constructor
클래스로 표현 (클래스 메타데이터를 통해 획득 가능)
- 생성자 조회
-
getConstructor(매개변수타입)
-
해당 클래스와 상위 클래스에서 상속된 모든 public 생성자 중 지정 생성자 조회
- e.g.
Constructor<?> constructor = aClass.getConstructor(String.class);
-
getDeclaredConstructor(매개변수타입)
-
해당 클래스에서 선언된 모든 생성자 중 지정 생성자 조회
- e.g.
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
-
getConstructors()
-
해당 클래스와 상위 클래스에서 상속된 모든 public 생성자를 반환
- e.g.
helloClass.getConstructors();
-
getDeclaredConstructors()
-
해당 클래스에서 선언된 모든 생성자를 반환
- 접근 제어자에 관계 X, 상속된 생성자 포함 X
- e.g.
helloClass.getDeclaredConstructors();
- 동적 인스턴스 생성
-
setAccessible(true)
-
private
생성자에 직접 접근해 호출할 수 있는 기능
- e.g.
constructor.setAccessible(true)
-
newInstance(인자)
- 생성자를 호출해 동적으로 객체 생성
- e.g.
Object instance = constructor.newInstance("hello")
Reference
김영한의 실전 자바 - 고급 2편, I/O, 네트워크, 리플렉션