본문 바로가기

IT/Java

자바 리플렉션(Java Reflection)

/* 
written by kaspy (kaspyx@gmail.com)
*/ 



자바 리플렉션(Reflection)이란 컴파일된 자바 코드에서 역으로 클래스를 불러서 메소드(Method) 및 변수(Field)를 구해오는 방법으로 클래스를 동적 로딩하여 사용할때 많이 사용되고 디컴파일할때에도 자주 사용되는 기법이다.


1. 리플렉션(Reflection) 기본


예를들어 아래와 같이 Class가 있다고 하면 생성자(Constructor)도 있을것이고, 필드(Field) 및 메쏘드(Method)도 있을것이다.


vi MyClass.java


  1. public class MyClass{
  2.  
  3.   private static String myName = null;
  4.   public String pubString = null;
  5.  
  6.   private void hiddenMethod()
  7.   {
  8.     System.out.print("hello private");
  9.   }
  10.  
  11.   public MyClass()
  12.   {
  13.   }
  14.  
  15.   public int mySum(int a,int b)
  16.   {
  17.     return a+b;
  18.   }
  19.  
  20.   public MyClass (String myname){
  21.     this.myName = myname;
  22.   }
  23.   public String getMyName()
  24.   {
  25.     return myName;
  26.   }
  27.  
  28.   public void setMyName(String myName)
  29.   {
  30.     this.myName = myName;
  31.   }
  32. } // javac MyClass.java


그렇다면 이를 사용하는 소스코드에서는 아래와같은 형식으로 사용된다.


vi MyJava.java


  1. import java.io.*;
  2.  
  3. public class MyJava
  4. {
  5.   public static void main(String args[])
  6.   {
  7.     MyClass myClass = new MyClass("kaspyx");
  8.  
  9.     String myName = myClass.getMyName();
  10.  
  11.     System.out.println("My name is "+myName);
  12.  
  13.   }
  14. } // javac MyJava.java

다시한번 설명을 하자면


MyClass myClass = new MyClass("kaspyx")

String myName = myClass.getMyName();


이런식으로 사용되는데, 이를 Reflection 으로 풀어보면 아래처럼 된다.

보통 Java 문법은 S(주어:object) V(동사:method) O(목적어:argument) 순서이지만, reflection 은 V.invoke(S,O) 순이라고 보면된다. 
먼저 클래스(Class)를 불러오고, 생성자(Constructor)를 구하고, 생성자에 인자를 넘겨 생성자를 만들고 메소드를 얻어와서 invoke() 함수로 실행하는 과정을 거친다. 이를 Reflection 으로 풀어보면 아래처럼 된다.

Class myClass = Class.forName("MyClass");
Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
Object myObj = myConstuctor.newInstance("kaspyx");
Method method = myClass.getMethod("getMyName");
String myName = (String)method.invoke(myObj);

코딩으로 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.     }
  17.     catch (Exception ex)
  18.     {
  19.       ex.printStackTrace();
  20.     }
  21.   }
  22. } // javac MyReflector.java

* 실행 결과화면


Reflection은 클래스의 생성자(Constructor)나 메소드에서 받는 인자 타입, 필드(Public 이냐 Private)냐 등등에따라서 불리워지는 타입에 맞춰줘야한다.  그래서 조금 헛갈린면이 없지않다

2. 메소드(Method) 및 인자값 전달하기

메소드가 위의 getMyName 처럼 인자가 String 하나면 위와같이 해줘야지만, 여러개의 String을 받는 경우라면 거기에맞게 짜줘야한다.
예를들어 setMyName(String myName) 형태라면 아래와같이해줘야한다.

Class[] methodParamClass = new Class[] {String.class};
Object[] methodParamObject = new Object[] {"Kyungsu kim"};
Method myMethod2 = getClass1.getMethod("getMyName2", methodParamClass);
myMethod2.invoke(myObj, methodParamObject);

mySum(int a, int b) 형태일 경우도 마찬가지이다. 만약  setMyName이에서 받는 문자열 인자가 여러개라면 컴마(,)로 나눠서 넘겨주면된다.

Class[] intParamClass = new Class[] {int.classint.class};
Object[] intParamObject = new Object[] {12};
Method method3 = myClass.getMethod("mySum",intParamClass);
int sum = (int)method3.invoke(myObj,intParamObject);

3. 필드(Field) 다루기

함수 호출 외에도 변수(필드)를 다룰때도 조금 다른데, setget을 이용한다.(여기서는 private 으로 선언된 myName 필드를 가지고 다루어볼것이다.)
priavte 필드(변수)에 접근하려 할때는 setAccessible(True)가 필요하며, getDeclaredField가 사용된다 아래를 참고해보기바란다.

Field field = myClass.getDeclaredField("myName");
field.setAccessible(true); // private일경우 secAccessible(true)로 해줘야 접근가능하다.
field.set(null,"No kaspy~!");
String myName2 = (String)field.get(myObj);

그럼 위에서 설명한 내용들(2,3)을 코딩에 반영해서 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.  
  17.       Class[] methodParamClass = new Class[] { String.class };
  18.       Object[] methodParamObject = new Object[] {"Kyungsu Kim"};
  19.       Method method2 = myClass.getMethod("setMyName",methodParamClass);
  20.       method2.invoke(myObj, methodParamObject);
  21.       myName = (String)method.invoke(myObj);
  22.  
  23.       System.out.println("Changed myname is "+ myName);
  24.  
  25.       Class[] intParamClass = new Class[] {int.classint.class};
  26.       Object[] intParamObject = new Object[] {12};
  27.       Method method3 = myClass.getMethod("mySum",intParamClass);
  28.       int sum = (int)method3.invoke(myObj,intParamObject);
  29.  
  30.       System.out.println("Sum is " + sum);
  31.  
  32.       Field field = myClass.getDeclaredField("myName");
  33.       field.setAccessible(true);
  34.       field.set(null,"No kaspy~!");
  35.       String myName2 = (String)field.get(myObj);
  36.       System.out.println("Myname2 is "+ myName2);
  37.  
  38.     }
  39.     catch (Exception ex)
  40.     {
  41.       ex.printStackTrace();
  42.     }
  43.   }
  44. } // javac MyReflector.java

* 실행 결과화면


4. 그외 리플렉션(Reflection) 함수들 


Java 에서는 위에서 설명한 함수들을 제공해주고있다. 


getMethods() , getFields() 등등을 사용하면 함수이나 변수이름을 몰라도 불러올수있으며, 이함수는 public으로 선언한 메소드(함수)나 필드(변수)를 가져온다. getMethod()는 문자열을 가지고 단일 함수를 찾아주지만 getDeclaredMethods()는 private 포함 클래스 내부에서 선언한 함수만 찾아준다.

비슷한 기능이지만 조금씩 다르니 레퍼런스를 잘 참고해서 용도에 맞게 써줘야할것이다. 


코딩을 통해 확인해보자.


vi MyReflector2.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector2
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.  
  10.       Class myClass = Class.forName("MyClass");
  11.       Method methods[] = myClass.getDeclaredMethods();
  12.  
  13.       System.out.println("1.DeclaredMethods() ================");
  14.       for (Method method: methods)
  15.       {
  16.         System.out.println("Found method name: " + method);
  17.       }
  18.  
  19.       Method methods2[] = myClass.getMethods();
  20.       System.out.println("2.getMethods() =====================");
  21.  
  22.       for (Method method: methods2)
  23.       {
  24.         System.out.println("Found method name: " + method);
  25.       }
  26.  
  27.       Field fields[] = myClass.getFields();
  28.       System.out.println("3.getFields=========================");
  29.  
  30.       for (Field field: fields)
  31.       {
  32.         System.out.println("Found field name: " + field);
  33.       }
  34.  
  35.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  36.       Method hiddenMethod = myClass.getDeclaredMethod("hiddenMethod");
  37.  
  38.       System.out.println("5.=================================");
  39.       System.out.println("Hidden method name is "+hiddenMethod.getName());
  40.     }
  41.     catch (Exception ex)
  42.     {
  43.       ex.printStackTrace();
  44.     }
  45.   }
  46. } // javac MyReflector2.java


* 실행 결과화면




5. 참고자료


https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html





'IT > Java' 카테고리의 다른 글

자바(Java)로 구현한 RSA 암호학 알고리즘 예제  (0) 2015.11.12
Java로 달력(Calendar) 구현하기  (0) 2015.05.18