Java/개념

리플랙션

어떤 기능을 가졌을까

리플랙션은 JVM에서 실행중인 애플리케이션의 런타임 동작을 제어하고 수정할 수 있는 기능입니다.

 

클래스의 필드목록이나 메소드목록, 상속하고있는 클래스 등등에 대한 정보를 알아낼 수 있으며 이를 런타임에 수정하는 것 또한 가능합니다.

 

리플랙션은 private 접근지시자를 포함한 모든 멤버에 접근이 가능한 것처럼 강력한 기능을 지니고있어서 사용시에 주의가 필요합니다.



사용하는 방법

리플렉션은 특정 클래스의 인스턴스나, FQCN, 클래스명 등을 이용하여 사용할 수 있습니다.

 

package me.ddings73;

public class Child extends Parent{
    public int A;
}
package me.ddings73;

public class App 
{
    public static void main( String[] args )
    {
        Class<? extends Child> aClass = new Child().getClass();
        //Class<Child> aClass = Child.class;
        //Class<?> aClass1 = Class.forName("me.ddings73.Child");
        System.out.println(aClass.getClassLoader());
        System.out.println(aClass.getName());
        System.out.println(aClass.getSuperclass());
    }
}
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
me.ddings73.Child
class me.ddings73.Parent

 

특정 클래스의 인스턴스나 생성자를 가지고 getClass() 명령을 사용해 리플렉션을 사용하는 것이 가능합니다.

  • 상위 클래스
  • 이름
  • 메소드 목록
  • 필드 목록
  • 생성자
  • etc..

Class<T> 의 인스턴스를 이용해서 얻을 수 있는 정보는 위와 같은 것들이 존재하며, 정보를 얻어오는 것 외에도 런타임시에 조작하는 것도 가능합니다.

 

리플랙션 에서의 필드

Class<T> 의 인스턴스를 이용하여 필드목록에 접근할 수 있는 메소드를 살펴봅시다.

 

getField(String name)

  • name과 같은 명칭의 필드를 현재 클래스가 상속하는 클래스를 포함하여 Field타입으로 반환
  • NoSuchFieldException 예외를 처리해줘야함.

getFields()

  • 모든 필드를 상속하는 클래스를 포함하여 Field[] 로 반환

getDeclaredField(String name)

  • 지정 클래스에서 선언된 name에 이름을 가진 필드를 반환
  • private 이더라도 반환됨
  • NoSuchFieldException 예외를 처리해줘야함.

getDeclaredFields()

  • 지정 클래스에서 선언된 모든 필드를 반환
  • private 이더라도 반환

 

 

Class<? extends Child> aClass = new Child().getClass();
for (Field field : aClass.getFields()) {
    System.out.println(field.getName());
}

= = =
public int me.ddings73.Child.A
public int me.ddings73.Parent.v

해당 메소드들은 기본적으로 반환타입이 Field이며 타입과 이름등을 알아낼 수 있습니다.

 

 

 

Child child = new Child();
Class<? extends Child> aClass = child.getClass();
for (Field field : aClass.getDeclaredFields()) {
    try {
        System.out.println(field.get(child));
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

해당 객체의 인스턴스가 존재한다면 get() 메소드를 사용하여 데이터도 알아낼 수 있으며, static 필드의 경우 null을 파라미터로 넘겨주면 됩니다.


private의 경우 기본적으로 접근이 불가능한데, 이 경우 접근지시자에 따라 setAccessable(true)를 이용하여 접근을 허락해주어야 합니다.

 

필드값을 설정하기 위해서는 set()을 이용하면 됩니다.

 

 

 

리플랙션에서의 메소드

 

getMethod(String name)

  • name의 명칭에 해당하는 메소드를 Method타입으로 반환
  • NoSuchMethodException 예외를 처리해줘야함.

getMethods()

  • Object 클래스를 포함한 모든 메소드를 반환

 

getDeclaredMethod(String name)

  • name의 명칭에 해당하면서 지정 클래스에서 선언된 메소드를 반환
  • private 이더라도 반환됨
  • NoSuchMethodException 예외를 처리해줘야함.

getDeclaredMethods()

  • 지정 클래스에서 선언된 모든 메소드를 반환
  • private 이더라도 반환됨

 

Child child = new Child();
Class<? extends Child> aClass = child.getClass();
Method method = aClass.getDeclaredMethod("sample02", int.class);
method.setAccessible(true);
method.invoke(child, 10);

메소드의 접근과 실행은 위와 같이 할 수 있습니다.

 

 

 

동적으로 객체 생성하기

Class<?> aClass = Class.forName("me.ddings73.Child");
Constructor<?> constructor = aClass.getConstructor(/*생성자의 파라미터 타입*/);
Child child = (Child) constructor.newInstance(/*파라미터 타입에 맞는 초기 값*/);

런타임시에 객체를 동적으로 생성하는 위해서는 getConstructer()를 이용하면 됩니다.
getConstructor() 사용 시에는 public 생성자만 호출할 수 있고, getDeclaredConstructer()사용 시에는 접근지시자 상관없이 생성자를 호출할 수 있습니다.

 

 

 

 

출처