(2) 리플렉션(Reflection) 사용하기

언어로그/Java 2011. 4. 7. 19:53



지난 포스팅에 이어 리플렉션을 사용하여 객체를 생성하고, private 필드에 접근하는 방법 그리고  메서드를 호출하는
방법에 대해 알아보자.



1. 리플렉션을 사용하여 객체생성
리플렉션을 사용하여 런타임에 클래스에 생성자들을 검사하고,  생성자 객체를 통해 객체를 생성하는 과정을 알아보자.
아래와 같이 3가지 단계를 거치게 되며 가장 먼저 클래스의 생성자 객체  java.lang.reflect.Consturctor 를 얻어야 한다. 


1.1  Constructor 객체 획득하기
다음과 같이 Class 객체로부터 Constructor 클래스를 얻는다. 
// 생성자 목록 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Constructor[] constructors = aClass.getConstructors();


Constructor[] 배열은 클래스에 선언된 모든 public 생성자의 Constructor 인스턴스를 가집니다.  특정한 파라미터를 갖는
특정한 생성자는 다음과 같이 얻을 수 있다. 
// 특정 파라미터를 갖는 생성자 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Constructor constructor =
        aClass.getConstructor(new Class[]{String.class});


위 예는 하나의 String 타입 파라미터를 갖는 생성자를 반환하는데, 일치하는 파라미터를 갖는 생성자가 없으면 
NoSuchMethodException 예외가 발생한다.


1.2 Constructor 파라미터 얻기  
 다음과 같이 생성자에 포함된 파라미터 타입 목록을 얻을 수 있다.
// 생성자의 파라미터 타입목록 얻기
Class aClass = ... // 이전에 얻은 클래스 객체
Class[] parameterTypes = constructor.getParameterTypes();


1.3 Constructor 객체를 사용하여 객체 생성하기
다음과 같이 생성자 객체로부터 객체를 생성한다. 
// 하나의 String 파라미터를 갖는 생성자를 얻는다. 
Constructor constructor = MyObject.class.getConstructor(String.class);
// 생성 
MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");

Constructor.newInstance() 메서드는 선택적인 개수의 파라미터를 취한다.  하지만 반드시 해당 생성자에 맞는 개수와 타입의 
파라미터를 제공해야 한다는 것에 주의하자.
 


 

2. 리플렉션을 사용하여 Field 에 접근하기
리플렉션을 사용하여 클래스의 모든 멤버 변수를 검사할 수 있으며, 런타임에 값을 얻어오거나 설정할 수 있다.
이때 하나의 프로퍼티 당 하나의 java.lang.reflect.Field 클래스 객체를 사용하게 된다. 

2.1 Field  객체 얻기 
다음과 같이 Field 객체를 얻는다. 
// 클래스에 선언된 public 속성의 Field 객체얻기
Class aClass = ... // 이전에 얻은 클래스 객체
Field[] fields = aClass.getFields();


Field[] 배열은  클래스에 선언된 각 public field  당 하나의 Field 객체를 갖는다.(public field 만을 갖는다는 것에 주의)
접근하려는 필드의 이름을 안다면,  다음과 같이 접근할 수  있다. 
// Field에 접근하기 
Class  aClass = MyObject.class
Field field = aClass.getField("someField");


위 예제는 아래 MyObject 에 선언된 someField 에 대응하는 Field 인스턴스를 반환합니다. 
public class MyObject{
  public String someField = null;
}

 getField() 메서드의 파라미터에 해당하는 이름의 필드가 클래스에 존재하지 않으면  NoSuchFieldException 예외가 발생한다.


2.2 Field 이름 얻기 
Field 인스턴스를 획득하면, 다음과 같이 Field.getName()을 사용하여 이름을 얻을 수 있다.
// Field 이름 얻기
Field field = ... // Field 객체를 얻는다. 
String fieldName = field.getName();


2.3 Field  타입 얻기
Field.getType() 메서드를 사용하여 필드의 타입을 얻을 수 있다. 
// Field 타입 얻기 
Field field = aClass.getField("someField");
Object fieldType = field.getType();


2.4 Field 값 조회하고 설정하기 
Field 에 대한 참조를 얻게되면, Field.get(), Field.set() 메소드를 사용하여 값을 얻거나 설정할수 있다.
// Field 값 설정하고 조회하기
Class  aClass = MyObject.class
Field field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance, value);

field.get(), set() 메서드에는 해당 필드를 소유하는 객체가 인자로 전달되야 하며 만일 static 필드라면 null을 전달한다.
 
  
2.5 Private Field 에 접근하기 
클래스의 Private 필드는 외부클래스에서 접근 할 수 없지만,  리플렉션을 사용하면 접근이 가능하다.  캡슐화를 깨는 동작일 
수 있지만, 단위테스트와 하이버네이트와 같은 프레임워크에서 유용하게 사용되기도 한다. 
private 필드에 접근하기 위해서는 Class.getDeclaredField(String name)와 Class.getDeclaredFields() 메서드를 사용한다. (Class.getField(String name) 와 Class.getFields() 메서드는 public 필드만을 반환한다.)

다음은 private field에 접근하는 예이다. 

Board board = new Board();
board.setContents("test contents...");
		
Field field = cls.getDeclaredField("contents");
field.setAccessible(true);
String contents = (String) field.get(board);
		
System.out.println("Private Contents Field: " + contents);

Field 객체의 setAccessible(true)를 호출하지 않고, private  필드값을 조회하려고 하면 IllegalAccessException 예외가 발생한다. 





3. 리플렉션을 사용하여 Method 호출하기
 java.lang.reflect.Method 클래스를 사용하여  메서드를 검사하고 호출 하는 방법을 알아보자.



3.1 Method 객체 얻기  
Method 클래스를 다음과 같이 획득한다.  
// Method 객체목록 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method[] methods = aClass.getMethods();


역시 클래스에 선언된 public 메서드당 하나의 Method 인스턴스를 갖으며, 메서드의 구체적인 파라미터 타입들을 알고 있으면
해당 메서드의 인스턴스를 얻을 수 있다. 다음은 String 파라미터 하나를 갖는 doSomething 메소드의 Method 객체를 얻는다.
// 파라미터를 갖는 메소드의 Method 객체 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method method = aClass.getMethod("doSomething", new Class[]{String.class});


만일 일치하는 메서드가 없으면, NoSuchMethodException이 발생한다. 파라미터가 없는 메서드를 얻기 위해서는 파라미터
배열 대신 null을 전달한다.
// 파라미터가 없는 메서드의 Method 객체 얻기 
Class aClass = ... // 이전에 얻은 클래스 객체
Method method = aClass.getMethod("doSomething", null);



3.2 Method 파라미터와 반환값 얻기
다음과 같이 해당 메서드의 파라미터들을 얻을 수 있다.
// 메서드의 파라미터 타입목록 얻기 
Method method = ... //
Class[] parameterTypes = method.getParameterTypes();

메서드의 반환타입은 다음과 같이 접근한다.
// 메서드의 반환값 타입 얻기 
Method method = ... //
Class returnType = method.getReturnType();



3.3  Method 객체를 사용하여 메서드 호출하기(Invoking)
다음과 같이 메서드를 호출할 수 있다.
// 메서드 호출 
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

invoke() 메소드에는 호출하기를 원하는 객체를 전달하며, static 메서드이면 null을 대신 전달한다.
Method.invoke(Object target, Object...parameters) 메서드는 선택적인 개수의 파라미터를 취하지만,  메서드가 필요로 하는
정확한 개수의 파라미터를 전달해야 한다. 


3.4 Private Method 에 접근하기 
Private Field 에 접근하는 것과 유사하게 Class.getDeclaredField(String name)와 Class.getDeclaredFields() 메서드를 사용한다. 
(Class.getField(String name) 와 Class.getFields() 메서드는 public 필드만을 반환한다.)


'언어로그 > Java' 카테고리의 다른 글

컬렉션(Collection)  (0) 2011.04.11
리플렉션으로 Getter 와 Setter 검사하기  (0) 2011.04.07
(2) 리플렉션(Reflection) 사용하기  (0) 2011.04.07
(1) 리플렉션(Reflection)  (3) 2011.04.07
예외처리 (Exception Handling)  (0) 2011.03.24
[Java] 어노테이션 사용하기  (1) 2011.03.19