Programming/JAVA

JAVA - Reflection

잇(IT) 2023. 5. 14. 17:51

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

MyClass myClass = new MyClass("asdf")

String myName = myClass.getMyName();

 

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

먼저 클래스(Class)를 불러오고, 생성자(Constructor)를 구하고, 생성자에 인자를 넘겨 생성자를 만들고 메소드를 얻어와서 invoke() 함수로 실행하는 과정을 거친다. 이를 Reflection 으로 풀어보면 아래처럼 된다.
 
Class myClass = Class.forName("MyClass");
Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
Object myObj = myConstuctor.newInstance("asdf");
Method method = myClass.getMethod("getMyName");
String myName = (String)method.invoke(myObj);

코딩으로 확인해보자.

import java.io.*;
import java.lang.reflect.*;
 
public class MyReflector
{
  public static void main(String args[])
  {
    try{
      Class myClass = Class.forName("MyClass");
      Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
      Object myObj = myConstuctor.newInstance("asdf");
      Method method = myClass.getMethod("getMyName");
      String myName = (String)method.invoke(myObj);
 
      System.out.println("Myname is "+ myName);
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
} // 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[] {"insoo baek"};
Method myMethod2 = getClass1.getMethod("getMyName2", methodParamClass);
myMethod2.invoke(myObj, methodParamObject);
 
mySum(int a, int b) 형태일 경우도 마찬가지이다. 만약  setMyName이에서 받는 문자열 인자가 여러개라면 컴마(,)로 나눠서 넘겨주면된다.
Class[] intParamClass = new Class[] {int.class, int.class};
Object[] intParamObject = new Object[] {1, 2};
Method method3 = myClass.getMethod("mySum",intParamClass);
int sum = (int)method3.invoke(myObj,intParamObject);
3. 필드(Field) 다루기
 
함수 호출 외에도 변수(필드)를 다룰때도 조금 다른데, set과 get을 이용한다.(여기서는 private 으로 선언된 myName 필드를 가지고 다루어볼것이다.)
priavte 필드(변수)에 접근하려 할때는 setAccessible(True)가 필요하며, getDeclaredField가 사용된다 아래를 참고해보기바란다.
 
Field field = myClass.getDeclaredField("myName");
field.setAccessible(true); // private일경우 secAccessible(true)로 해줘야 접근가능하다.
field.set(null,"No insoo baek");
String myName2 = (String)field.get(myObj);
 
그럼 위에서 설명한 내용들(2,3)을 코딩에 반영해서 확인해보자.
import java.io.*;
import java.lang.reflect.*;
 
public class MyReflector
{
  public static void main(String args[])
  {
    try{
      Class myClass = Class.forName("MyClass");
      Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
      Object myObj = myConstuctor.newInstance("asdf");
      Method method = myClass.getMethod("getMyName");
      String myName = (String)method.invoke(myObj);
 
      System.out.println("Myname is "+ myName);
 
      Class[] methodParamClass = new Class[] { String.class };
      Object[] methodParamObject = new Object[] {"insoo baek"};
      Method method2 = myClass.getMethod("setMyName",methodParamClass);
      method2.invoke(myObj, methodParamObject);
      myName = (String)method.invoke(myObj);
 
      System.out.println("Changed myname is "+ myName);
 
      Class[] intParamClass = new Class[] {int.class, int.class};
      Object[] intParamObject = new Object[] {1, 2};
      Method method3 = myClass.getMethod("mySum",intParamClass);
      int sum = (int)method3.invoke(myObj,intParamObject);
 
      System.out.println("Sum is " + sum);
 
      Field field = myClass.getDeclaredField("myName");
      field.setAccessible(true);
      field.set(null,"No insoo baek");
      String myName2 = (String)field.get(myObj);
      System.out.println("Myname2 is "+ myName2);
 
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
} // javac MyReflector.java

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

 

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

 

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

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

 

코딩을 통해 확인해보자.

import java.io.*;
import java.lang.reflect.*;
 
public class MyReflector2
{
  public static void main(String args[])
  {
    try{
 
      Class myClass = Class.forName("MyClass");
      Method methods[] = myClass.getDeclaredMethods();
 
      System.out.println("1.DeclaredMethods() ================");
      for (Method method: methods)
      {
        System.out.println("Found method name: " + method);
      }
 
      Method methods2[] = myClass.getMethods();
      System.out.println("2.getMethods() =====================");
 
      for (Method method: methods2)
      {
        System.out.println("Found method name: " + method);
      }
 
      Field fields[] = myClass.getFields();
      System.out.println("3.getFields=========================");
 
      for (Field field: fields)
      {
        System.out.println("Found field name: " + field);
      }
 
      Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
      Method hiddenMethod = myClass.getDeclaredMethod("hiddenMethod");
 
      System.out.println("5.=================================");
      System.out.println("Hidden method name is "+hiddenMethod.getName());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
} // javac MyReflector2.java
728x90

'Programming > JAVA' 카테고리의 다른 글

JAVA - Optional  (0) 2023.06.05
JAVA - Reflection API  (0) 2023.06.01
JAVA - 람다 (기본)  (0) 2023.05.18
JAVA - 컬렉션 프레임 워크 (Collections Framework), List, Set, Map ... (1) 그 중 List  (0) 2023.05.18
JAVA - Class 클래스  (0) 2023.05.14