리플랙션
어떤 기능을 가졌을까
리플랙션은 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()사용 시에는 접근지시자 상관없이 생성자를 호출할 수 있습니다.