Backend/Java

Java - Reflection

elevne 2023. 5. 27. 21:36

JavaReflectionClass, Interface, 필드 및 메서드의 런타임 속성을 검사, 수정할 수 있게끔 해주는 API 이다. Compile 시에 이름을 모를 때 특히 유용하게 사용될 수 있다고 한다. 또, Reflection 을 사용하여 새로운 Object 를 생성, 메소드를 호출하고 filed 에 값을 세팅해줄 수도 있다.

 

 

Reflection동적 바인딩, Dynamic Binding 기능을 지원하는 것이다. 바인딩이란 프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정짓는 행위를 의미하는데, 정적 바인딩과 동적 바인딩으로 나뉜다. 정적 바인딩은 Compile 시 진행되는 것으로 프로그램 실행 중 변하지 않는다. 동적 바인딩은 Runtime 시 결정되는 것으로 Java 에서 다형성과 상속이 가능하게끔 해주는 것이다. Reflection 은 동적 바인딩을 활용, 동적으로 Class 를 사용할 수 있게끔 해준다.

 

 

 

우선 기본적인 Reflection 의 사용법에 대해 알아보았다.

 

 

 

Class<?> clazz = Class.forName("reflection.Goat");
System.out.println(clazz.getSimpleName());
System.out.println(clazz.getName());

 

 

result

 

 

Class 객체Class.forName 메소드를 통해 생성하고 getSimpleName(), getName() 등의 메소드로 Class 의 이름을 반환받을 수 있다.

 

 

 

Class<?> goatClass = Class.forName("reflection.Goat");
Class<?> animalClass = Class.forName("reflection.Animal");
int goatMods = goatClass.getModifiers();
int animalMods = animalClass.getModifiers();
System.out.println(Modifier.isPublic(goatMods));
System.out.println(Modifier.isAbstract(animalMods));

 

 

result

 

 

Class 객체getModifiers() 메소드를 사용하여 제어자에 대한 정보를 얻을 수 있다. 이는 int 로 반환된다. Modifier 의 정적메소드들 (isPublic, isAbstract 등) 을 사용하여 Modifier 에 대한 검증을 진행해볼 수도 있다.

 

 

 

Class<?> goatClass2 = goat.getClass();
Package pkg = goatClass2.getPackage();
System.out.println(pkg.getName());

Class<?> superClass = goatClass.getSuperclass();
System.out.println(superClass.getSimpleName());

 

 

result

 

 

getPackage(), getSuperclass() 메소드로 패키지, 슈퍼클래스에 대한 정보도 얻을 수 있다.

 

 

 

Class<?>[] interfaces = goatClass.getInterfaces();
for (Class cls : interfaces){
    System.out.println(cls.getSimpleName());
}
Constructor<?>[] constructors = goatClass.getConstructors();
for (Constructor<?> constructor : constructors){
    System.out.println(constructor.getName());
}
Field[] fields = goatClass.getFields();
for (Field field : fields){
    System.out.println(field.getName());
}
Method[] methods = goatClass.getDeclaredMethods();
for (Method method : methods){
    System.out.println(method.getName());
}

 

 

그 외에도 기본적으로 interface, 생성자, 필드, 메소드에 대한 정보를 얻어낼 수 있다. 이 때, getFields 와 같은 메소드를 getDeclaredFields 와 같이 변경해주면 public 으로 선언되지 않은 정보들에 대해서도 조회가 가능하다.

 

 

 

public static void main(String[] args) throws Exception {

    Class<?> clazz = Class.forName("reflection.Person");
    Constructor constructor = clazz.getConstructor(String.class, int.class);
    Person person = (Person) constructor.newInstance("WONIL", 24);
    Method introduction = person.getClass().getDeclaredMethod("introduce");
    introduction.setAccessible(true);
    introduction.invoke(person);

}

 

 

result

 

 

위 코드는 우선 forName 메소드로 해당 위치에 있는 클래스를 생성한다. 해당 클래스의 String, int 를 매개변수로 하는 생성자를 불러온다 (Contructor 객체). 그 다음 그 생성자에 newInstance() 메소드를 사용하여 해당 클래스를 생성한다. 또, 해당 클래스에서 getDeclaredMethod() 를 통해 인자로 넘긴 "introduce" 라는 이름에 해당하는 private 메소드를 불러온다. private 메소드는 사용하기 위해 setAccessible(true) 를 해주어야 한다. 그 다음 invoke() 를 통해 Method 를 실행시킨다.

 

 

 

ReflectionRuntime 시점에서 클래스의 인스턴스를 생성하여 접근 제어자와 관계 없이 필드와 메소드에 접근하여 필요한 작업을 수행할 수 있다는 장점을 가진다. 하지만 Reflection 은 캡슐화를 저해하며 런타임 시점에서 인스턴스를 생성하기에 컴파일 시 타입 체크가 되지 않으며 구체적인 동작 흐름을 파악하기 쉽지 않다. 또, 단순히 필드 및 메소드에 접근할 때보다 이를 이용할 때 성능이 저하될 수 있다.

 

 

 

아래는 실제 상황에서 Reflection 을 활용하는 예제 코드이다. 조건에 따라 같은 인터페이스를 구현하는 다른 객체를 반환해야 하는 경우를 생각해본다. 일반적인 경우에는 if 문 혹은 switch / case 를 활용하여 객체를 각각 따로따로 생성할 것이다. Reflection 을 활용하여 길어질 수 있는 조건문을 간결하게 작성해볼 수 있다. 아래와 같이 작성될 수 있을 것이다.

 

 

public Eating getEating(String name) {
    try {
        Class clazz = Class.forName(name);
        Constructor constructor = clazz.getConstructor();
        return (Eating) constructor.newInstance();
    } catch (Exception e) {
        throw new RuntimeException("NO CLASS");
    }
}

 

 

 

 

 

 

 

Reference:

https://www.baeldung.com/java-reflection

https://velog.io/@alsgus92/Java-Reflection%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%96%B8%EC%A0%9C%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C

https://steady-coding.tistory.com/609

https://velog.io/@alsgus92/Java-Reflection%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%96%B8%EC%A0%9C%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C