Java Reflection 의 사용
reflection 은 자바의 특징이다. 실행중인 자바프로그램 내부를 검사하고 내부의 속성을 수정할 수 있도록 한다. 예를 들어, 어떤 자바클래스가 가진 모든 멤버의 이름을 얻거나 보여줄 수 있다.
자바에서 클래스가 그 자신을 조사하고 수정하는 것이 많다고 할수는 없으나 다른 언어에서는 볼수 없는 특징이다.
reflection 이 구체적인 쓰임중에 하나가 빌더툴을 이용해서 소프트웨어 콤포넌트를 만드는 곳에서 이다. 툴은 reflection 을 사용해서 동적으로 로딩되는 자바 콤포넌트(클래스)의 속성을 얻을 수 있다.
예제
import! java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[])
{
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
명령
java DumpMethods java.util.Stack
실행 결과
public java.lang.Object java.util.Stack.push(
java.lang.Object)
public synchronized
java.lang.Object java.util.Stack.pop()
public synchronized
java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized
int java.util.Stack.search(java.lang.Object)
이 프로그램은 java.util.Stack 의 속성과 반환값에 따라 메소드리스트를 출력한다.
이 프로그램은 Class.forName 을 통해서 클래스를 로딩하고 getDeclaredMethods 을 통해서 클래스에서 정의한 메소드리스트를 얻는다. java.lang.reflect.Method 는 단일 메쏘드를 나타내는 클래스이다.
Reflection 을 사용한 Set up
Method 와 같은 reflection class 는 java.lang.reflect 에 있다. 이 클래스를 사용하기 위해서는 세가지 스텝을 밟아야 한다.
첫번째는 수정하기를 원하는 클래스의 java.lang.Class 객체를 얻어야 한다. java.lang.Class 는 클래스를 표현하고, 실행중인 자바 프로그램과 인터페이스한다.
자바기본형에 대한 클래스 정보를 얻는 방법.
방법 1 :
Class c = Class.forName("java.lang.String");
방법 2 :
Class c = int.class;
방법 3 : 기본형의 경우 (Integer 와 같은) Wrapper 에 기정의된 TYPE을 사용한다.
Class c = Integer.TYPE;
두번째 스텝은 getDeclaredMethods와 같은 메소드를 Call 해서, 클래스에 정의된 모든 메소드의 리스트를 얻는다.
세번째 스텝은 정보 수정을 위해 Reflection API 를 이용한다.
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
String 에 선언된 첫번째 메소트를 보여준다.
Simulating the instanceof Operator
클래스 정보가 있으면, 다음 스텝은 클래스 객체에 대해서 기본적인 질의다. Class.isInstance 메쏘드는 instanceof 를 통해서 구현될 수 있다.
class A {}
public class instance1 {
public static void main(String args[])
{
try {
Class cls = Class.forName("A");
boolean b1
= cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
A의 클래스객체가 만들어진다. 그리고 class instance objects 가 A 의 인스턴스인지 체크한다.
Integer(37) 은 아니지만, new A() 는 True 이다.
클래스 메쏘드 찾기
reflection 의 가장 기초적인 쓰임은 클래스에서 정의한 메소드가 무엇인지 찾아내는 것이다. 이를 위해서 다음 코드가 사용될 수 있다.
import! java.lang.reflect.*;
public class method1 {
private int f1(
Object p, int x) throws NullPointerException
{
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method1");
Method methlist[]
= cls.getDeclaredMethods();
for (int i = 0; i < methlist.length; i++) {
Method m = methlist[i];
System.out.println("name = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
Class pvec[] = m.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println(" param #" + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("return type = " + m.getReturnType());
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
getDeclaredMethods 을 통해서 메쏘드 리스트를 조회한다. getDeclaredMethods 대신에 getMethods 를 사용하면 상속된 메쏘드에 대한 정보를 얻을 수 있다.
실행결과
name = f1
decl class = class method1
param #0 class java.lang.Object
param #1 int
exc #0 class java.lang.NullPointerException
return type = int
-----
name = main
decl class = class method1
param #0 class [Ljava.lang.String;
return type = void
-----
Constructors 에 대한 정보얻기
import! java.lang.reflect.*;
public class constructor1 {
public constructor1()
{
}
protected constructor1(int i, double d)
{
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor1");
Constructor ctorlist[]
= cls.getDeclaredConstructors();
for (int i = 0; i < ctorlist.length; i++) {
Constructor ct = ctorlist[i];
System.out.println("name
= " + ct.getName());
System.out.println("decl class = " +
ct.getDeclaringClass());
Class pvec[] = ct.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #"
+ j + " " + pvec[j]);
Class evec[] = ct.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println(
"exc #" + j + " " + evec[j]);
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
이 프로그램에서는 리턴타입정보가 없는데... 생성자는 리턴타입을 갖지 않기 때문이다.
실행결과
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
Class Fields 찾기
import! java.lang.reflect.*;
public class field1 {
private double d;
public static final int i = 37;
String s = "testing";
public static void main(String args[])
{
try {
Class cls = Class.forName("field1");
Field fieldlist[]
= cls.getDeclaredFields();
for (int i
= 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
System.out.println("name
= " + fld.getName());
System.out.println("decl class = " +
fld.getDeclaringClass());
System.out.println("type
= " + fld.getType());
int mod = fld.getModifiers();
System.out.println("modifiers = " +
Modifier.toString(mod));
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
이전예제와 비슷하다. 새로운 특징은 modifier 의 사용이다.
modifier는 'private int' 와 같은 필트멤버를 표현하기위한 reflection class 이다.
modifier 는 숫자로 표현된다. Modifier.toString 은 'final' 앞의 'static' 과 같은 선언순서의 문자열표현을 리턴한다.
실행결과 :
name = d
decl class = class field1
type = double
modifiers = private
-----
name = i
decl class = class field1
type = int
modifiers = public static final
-----
name = s
decl class = class field1
type = class java.lang.String
modifiers =
-----
이름으로 메쏘드 실행하기.
import! java.lang.reflect.*;
public class method2 {
public int add(int a, int b)
{
return a + b;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod(
"add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj
= meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
프로그램이 add 라는 메쏘드를 실행시키는데 실행시까지 알지 못한다고 가정해보자. 메쏘드 이름은 실행시간에 알수있다.
getMethod 는 클래스에서 두개의 숫자 파라미터와 해당 이름을 가진 메쏘드를 찾아낸다.
새로운 객체 만들기
import! java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
필드값 바꾸기
import! java.lang.reflect.*;
public class field2 {
public double d;
public static void main(String args[])
{
try {
Class cls = Class.forName("field2");
Field fld = cls.getField("d");
field2 f2obj = new field2();
System.out.println("d = " + f2obj.d);
fld.setDouble(f2obj, 12.34);
System.out.println("d = " + f2obj.d);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
배열의 사용
import! java.lang.reflect.*;
public class array1 {
public static void main(String args[])
{
try {
Class cls = Class.forName(
"java.lang.String");
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "this is a test");
String s = (String)Array.get(arr, 5);
System.out.println(s);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
import! java.lang.reflect.*;
public class array2 {
public static void main(String args[])
{
int dims[] = new int[]{5, 10, 15};
Object arr
= Array.newInstance(Integer.TYPE, dims);
Object arrobj = Array.get(arr, 3);
Class cls =
arrobj.getClass().getComponentType();
System.out.println(cls);
arrobj = Array.get(arrobj, 5);
Array.setInt(arrobj, 10, 37);
int arrcast[][][] = (int[][][])arr;
System.out.println(arrcast[3][5][10]);
}
}
=======================================================================================
try {
Class c = Class.forName(className);
Class c = Class.forName(className);
Class partypes[] = new Class[1];
partypes[0]=JVPMainUI.class;
Constructor constructor = c.getConstructor(partypes);
Object arglist[] = new Object[1];
arglist[0] = _mainUI ;
Object obj = constructor.newInstance(arglist);
//축약
// Class partypes[] = new Class[1];
// Constructor constructor = c.getConstructor(new Class[] {JVPMainUI.class});
// Object obj = constructor.newInstance(new JVPMainUI[] {_mainUI});
//인자없는 생성자호출하려면..
//Constructor constructor = c.getConstructor(new Class[] {});
//Object obj = constructor.newInstance(new Object[] {});
Runnable runner = null;
if (obj instanceof JFrame) {
runner = new WindowShower( (JFrame) obj);
}else if (obj instanceof JDialog) {
runner = new WindowShower( (JDialog) obj);
}
EventQueue.invokeLater(runner);
}catch(ClassNotFoundException cnf){
JOptionPane.showMessageDialog(_mainUI,className +" [존재하지 않는 클래스]" , "^^*",JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception ee) {
ee.printStackTrace();
}
댓글 없음:
댓글 쓰기