Reflection이란?
구체적인 Class Type을 알지 못하더라도 해당 Class의 method, type, variable들에 접근할 수 있도록 해주는 자바 API.
컴파일된 바이트 코드를 통해 Runtime에 동적으로 특정 Class의 정보를 추출할 수 있는 프로그래밍 기법.
그럼 "동적으로 Class의 정보를 추출한다는 것"은 무슨말일까? >> Binding (바인딩)
바인딩이란? 프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정하는 것. 즉, 프로그램에서 사용되는 변수나 메소드 등 모든 것들이 결정되도록 연결해주는 것을 뜻한다. 이는 결정짓는 시점에 따라 "정적 바인딩" , " 동적 바인딩"으로 나뉜다.
Reflection은 언제 사용할까?
- 동적으로 Class를 사용해야할 경우 : 코드 작성 시점에서는 어떠한 Class를 사용해야할지 모르지만, Runtime에 Class를 가져와서 실행해야하는 경우
- Test Code 작성 : Private 변수를 변경하고 싶거나 Private Method를 테스트할 경우
- 자동 Mapping 기능 구현 : IDE 사용시 입력만 해도 관련된 Class 혹은 Method 목록들을 IDE가 먼저 확인하고 자용자에게 제공함
- Jackson, GSON 등의 JSON Serialization Library : Reflection을 사용하여 객체 필드의 변수명/어노테이션명을 Json key와 mapping 해주고 있음.
Reflection 사용 방법
Reflection을 통해 "Class/Interface, Constructor, Method, Field" 정보를 얻을 수 있으며, 해당 정보들을 통해 객체생성/메소드호출/변수 값을 변경할 수 있다.
[1] Class / Interface
: Class의 이름만 알고 있는 경우에도 정보를 가져화 객체 생성이 가능하며, class에 구현된 interface 정보도 가져올 수 있음.
public static void main(String[] args) throws Exception {
// 1. class를 알고 있을 경우
Class car = Car.class;
// 2. class 이름만 알고 있을 경우
Class car = Class.forName("com.reflection.test.Car");
// class.getName() -> com.reflection.test.Car
// 3. Default 생성자를 이용한 객체 생성
Car realCar = car.newInstance();
// 4. class에 구현된 interface 확인
Class[] interfaces = car.getInterfaces();
}
[2] Constructor
: Class의 이름으로 클래스 정보를 가져오고, 해당 객체의 생성자를 가져와 객체 생성이 가능함.
public static void main(String[] args) throws Exception {
Class car = Class.forName("com.reflection.test.Car");
// 1. 인자가 없는 생성자 가져오기
Constructor constructor = car.getDeclaredConstructor();
// 2. String 인자를 가진 생성자 가져오기
Constructor constructor = car.getDeclaredConstructor(String.class);
// 3. 모든 생성자 가져오기
Constructor constructors[] = car.getDeclaredConstructors();
// 4. public 생성자만 가져오기
Constructor constructors[] = car.getConstructors();
// public com.reflection.test.Car()
// public com.reflection.test.Car(java.lang.String)
// 5. 생성자를 이용한 객체 생성
Car realCar = constructor.newInstance();
}
[3] Method
: Class의 이름으로 클래스 정보를 가져오고, Method 명으로 메소드도 가져올 수 있음.
public static void main(String[] args) throws Exception {
Class car = Class.forName("com.reflection.test.Car");
// 1. 인자가 없는 method 가져오기
Method method = car.getDeclaredMethod("move");
// 2. String 인자를 가진 method 가져오기
Method method = car.getDeclaredMethod("move", String.class);
// 3. 모든 method 가져오기
Method methods[] = car.getDeclaredMethods();
// 4. 상속받은 method와 public method 가져오기
Method methods[] = car.getMethods();
// public void com.reflection.test.Car.move()
// public void com.reflection.test.Car.move(java.lang.String)
// 5. method 호출
Class realCar = car.newInstance();
method.invoke(realCar, /*인자*/);
// 6. 접근 제한자를 무시한 method 호출.
method.setAccessible(true);
method.invoke(realCar, /*인자*/);
}
>> invoke함수란? : 메소드 명만 받아서 정의되어있는 메소드 중 내가 원하는 메소드만 실행시키고 싶을 때 사용하는 것.
[4] Filed
public static void main(String[] args) throws Exception {
Class car = Class.forName("com.reflection.test.Car");
// 1. car 객체에서 name 에 해당하는 field 가져오기
Field field = car.getDeclaredField("name");
// 2. car + car super 객체를 포함하여 name에 해당하는 field 가져오기
Field field = car.getField("name");
// 3. car 객체에 선언된 모든 field 가져오기
Field[] fields = car.getDeclaredFields();
// private java.lang.String com.reflection.test.Car.name
// public java.lang.Integer com.reflection.test.Car.type
// 4. car + car super 객체의 모든 public field 가져오기
Field[] fields = car.getFields();
// public java.lang.Integer com.reflection.test.Car.age
}
[5] Filed값 변경
ublic static void main(String[] args) throws Exception {
Class class = Class.forName("com.reflection.test.Car");
Constructor constructor = class.getConstructor()
Car car = constructor.newInstance()
Field field = car.getField("name");
// 1. public field 일 경우
field.set(car, "신형차");
// 2. private field 일 경우
field.setAccessible(true);
field.set(car, "신형차");
}
Reflection의 장단점
# 장점
- 확장성
대량의 if/else문을 사용하는 것보다 Reflection을 이용하여 재사용 가능한 컴포넌트로 만들 수 있음 - Class 브라우저 및 시각적 개발 환경을 제공
Reflection을 통해 Method, Property, Constructor를 미리 파악함으로써 사용할 정보를 열거하여 시각적 개발 환경을 구축할 수 있음 - 디버거 및 테스트 도구
디버거는 개인 Property, Method, Constructor를 검사할 수 있어야 하는데, 테스트 장치는 Reflection을 사용하여 클래스에 정의된 발견 가능한 세트 API를 체계적으로 호출하여 테스트에서 높은 수준의 코드 커버리지를 보장할 수 있음 - 라이브러리 파악
Java에서 지원하는 라이브러리가 아닌 특정 기업의 라이브러리를 사용하는 경우 해당 라이브러리에 존재하는 클래스 및 메서드를 분석할 수 있음.
# 단점
- 컴파일 시점에 가능한 타입 확인이 불가능하여 캄파일 시에 타입 확인이나 예외 검사를 할 수 없음.
>> 즉, 컴파일 에러가 아닌 런타임시에 에러가 발생하기 때문에 코드 운용에 위험이 있음.
- 클래스, 메소드, 필드를 접근하여 직접 이용하기 때문에 객체 지향 프로그래밍의 특징인 추상화를 위반함.
* 참고 사이트
[Java] Reflection은 무엇이고 언제/어떻게 사용하는 것이 좋을까?
'[IT] > JAVA' 카테고리의 다른 글
[JAVA] String 값 Boolean으로 변경하기 (0) | 2023.03.27 |
---|---|
[JAVA] OOP(객체 지향 프로그래밍)의 개념과 특징 (장단점/추상화/다형성/캡슐화/상속) (0) | 2022.05.04 |
[JAVA] Window10 JAVA 환경 변수 설정하기 (환경 변수 설정 이유) (0) | 2021.11.18 |
[REST][REST API] REST란 무엇일까? (0) | 2020.12.29 |
[Java] 자바 데이터 타입, 변수 그리고 배열 / Primitive / reference / literal (0) | 2020.12.06 |