Java의 Reflection은 로딩이 완료된 클래스에서 또 다른 클래스를 동적으로 로딩(Dynamic Loading)하여 constructor, member field, member method 등을 사용할 수 있게 해준다. RTTI 같은 기법도 Reflection의 기능이다. Reflection은 C나 C++ 등의 언어에는 없다.
import java.util.*;
class DynamicLoading
{
public static void main(String[] args)
{
String name = "java.util.Stack";
try
{
Class what = Class.forName(name);
Stack s = (Stack)what.newInstance();
s.push(10);
s.push(20);
System.out.println(s);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
위 프로그램은 간단하게 Dynamic Loading의 과정을 보여주는 예시이다. Class 클래스의 forName이라는 메서드로 클래스 내의 멤버들에 대한 자세한 정보를 동적으로 로드한다.
Java API 에서의 forName(String className) 메서드에 대한 description : Returns the Class object associated with the class or interface with the given string name.
말하자면, 문자열 형태의 클래스명을 parameter로 받고 그 이름의 클래스나 인터페이스와 관련된 클래스 객체를 리턴해주는 것이다.
그리고 그렇게 불러온 클래스를 원하는 타입의 클래스에 newInstance() 메서드를 통해 Type casting 하여 사용할 수 있는 것이다.
API Class 클래스의 newInstance() : Creates a new instance of the class represented by this Class object.
말 그대로 표현하고자 하는 클래스 객체를 생성해주는 메서드이다.
위와 같이 생성된 Stack 객체 s는 스택의 역할을 수행할 수 있다.
다음으로 Method 클래스의 배열을 활용해 넘겨받은 Object의 메서드 정보들을 알아내보겠다.
import java.lang.reflect.*;
import java.util.*;
public class JavaReflection {
public static void main(String[] args) {
HashMap<String, Integer> l = new HashMap<String, Integer>();
l.put("Kim", 10);
identify(l);
}
static void identify(Object o) {
Class c = o.getClass();
Method[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals("isEmpty")) {
try {
Object[] args = new Object[0];
Object result = methods[i].invoke(o, args);
System.out.println(result);
}
catch(Exception ex) {
}
break;
}
}
}
}
해시 맵을 사용하여 데이터를 하나 넣어주고 identify를 해준다.
Class c는 parameter로 넘겨받은 Object o에서 getClass()를 한다.
java.lang의 최상의 클래스 Object에서 getClass()를 한다면 Returns the runtime class of this object.
즉 현재 클래스 정보를 가져와서 리턴해준다. 그래서 c는 HashMap이 될 것이고, Class의 메서드인 getDeclaredMethod()를 실행해서 Method 배열에 담아주도록 한다. 리턴 타입이 Method[]인 getDeclaredMethod()는 Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods. 로 API에서 설명한다. 대충 특정 클래스나 인터페이스에서 선언된 protected와 private을 포함하는 모든 메서드들을 (단, 상속된 메서드는 제외하고) Method array로 리턴해준다는 뜻이다.
저장된 배열 methods에는 /* public boolean java.util.HashMap.remove(java.lang.Object,java.lang.Object)
public java.lang.Object java.util.HashMap.remove(java.lang.Object)
public java.lang.Object java.util.HashMap.get(java.lang.Object)
public java.lang.Object java.util.HashMap.put(java.lang.Object,java.lang.Object)
public java.util.Collection java.util.HashMap.values() ... */ 등의 메서드들이 class path와 함께 담겨져 있다.
마지막으로 이렇게 담겨 있는 methods 배열을 traverse하면서 isEmpty라는 메서드 name을 가진 객체를 만났을때, 그 객체를 invoke하여 결과를 출력까지 해보겠다.
API 상에서의 Method 클래스의 invoke(Object obj, Object... args) 메서드는 Invokes the underlying method represented by this Method object, on the specified object with the specified parameters. 라고 설명하는데 대충 특정 매개변수를 사용한 지정된 객체에서 methods[i]가 가리키는 기본 메서드를 호출한다 라는 뜻이다.
if문에 methods[i].getName().equals("isEmpty") 를 넣고 돌린다면 result는 false가 찍혀나오고, methods[i].getName().equals("values") 같은 걸로 한다면 [10]이 나오는 것을 확인할 수 있다.
댓글