[IT]/JAVA

[JAVA] Reflection이란? (언제 사용하고/어떻게 사용하고/장단점은?)

ee2ee2 2023. 11. 23. 12:49
728x90
반응형

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 무엇이고 언제/어떻게 사용하는 것이 좋을까?