Java

JAVA Reflection Use Cases

1. 해당 클래스에 등록된 모든 메서드 가져오기

@Test
    void run() throws Exception {
        Class<Junit3Test> clazz = Junit3Test.class;

        Method[] methods = clazz.getMethods();
    }

 

2. 해당 클래스에 등록된 메서드 명 가져오기

for (Method method: methods) {
     if (method.getName().startsWith("test")) {
        // method 명이 "test"로 시작하는 메서드를 가져온다.
     }
}

 

3. 메서드 실행시키기

method.invoke(clazz.getDeclaredConstructor().newInstance());

 

4. 해당 클래스에 등록된 메서드명에 특정 어노테이션이 포함되어있는 메서드 가져오기

@Test
    void run() throws Exception {
        Class<Junit4Test> clazz = Junit4Test.class;
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
        // @MyTest 라는 어노테이션이 존재하면 실행시킨다.
            if (method.isAnnotationPresent(MyTest.class)) {
                method.invoke(clazz.getDeclaredConstructor().newInstance());
            }
        }
    }

 

5. 인자로 들어온 prefix가 붙은 패키지를 스캔하기 위한 생성자

// main/java에서 examples prefix가 붙은 패키지를 scan한다.
Reflections reflections = new Reflections("examples");

 

6. Annotation이 붙은 클래스를 찾아 log로 출력하기

Set<Class<?>> classesWithController = reflections.getTypesAnnotatedWith(Controller.class);
    classesWithController.forEach(
        c -> log.info(">>> @Controller at {}", c.getSimpleName())
    );

 

7. Clazz에서 get메서드를 사용하면?

final Class<Question> clazz = Question.class;

// Returns the simple name of the underlying class as given in the source code. Returns an empty string if the underlying class is anonymous.
assertThat(clazz.getSimpleName()).isEqualTo("Question");

// Returns the name of the class or interface represented by this object.
assertThat(clazz.getName()).isEqualTo("reflection.Question");

// Returns the canonical name of the underlying class as defined by the Java Language Specification
assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question");

 

8. 주어진 string name과 연관된 클래스를 가져온다. getName, getSimpleName등은 7과 같이 동작한다.

reflection.Question이 아닌, Question만 작성하면 ClassNotFoundException이 터진다. 상위 패키지를 명시해주어야한다.

// Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
// Class.forName(className, true, currentLoader)

final Class<?> clazz = Class.forName("reflection.Question");

 

9. Class에 선언된 모든 필드를 가져오기

final Object student = new Student();
final Field[] fields = student.getClass().getDeclaredFields();
  • getDeclaredFields(): Class 객체가 나타내는 인터페이스 또는 클래스에 의해 선언된 모든 필드를 반영하는 Field 객체의 배열을 반환한다. public, protected, default(package) access, private fields가 포함되며 상속된 필드는 제외된다. 반환 배열의 요소는 정렬되어있지 않으며 특정 순서로 되어있지 않다.
  • getFields(): Class 객체가 나타내는 클래스 또는 인터페이스의 액세스 가능한 모든 공용 필드를 반영하는 Field 객체를 포함하는 배열을 반환한다.

10. Class에 선언된 모든 메서드를 가져오기

final Class<?> animalClass = Student.class;
final Method[] methods = animalClass.getDeclaredMethods();
  • getDeclaredMethods(): public, protected, 기본 패키지 액세스, private 메서드를 포함해 이 Class에 선언된 모든 메서드를 가져온다. 대신, 상속된 메서드는 제외된다.
  • getMethods(): Super Class 및 Super Interface에서 상속된 것을 포함해 이 클래스의 모든 public 메서드를 반환한다. 단, clone()에 대한 메서드는 포함되어있지 않다. (실제로 테스트해보면 getClass, toString 등 모든 것이 포함된다.)

11. Class에 선언된 모든 생성자 가져오기

final Class<?> questionClass = Question.class;
final Constructor<?>[] constructors = questionClass.getConstructors();
  • getDeclaredConstructors(): public, protected, 기본 패키지 액세스, private 메서드를 포함해 이 Class에 선언된 모든 생성자를 가져온다.
  • getConstructors(): 이 클래스의 모든 public constructors의 배열을 반환한다.

 

12. Class에 선언된 특정 생성자를 가져와서 객체 생성하기

final Class<?> questionClass = Question.class;

final Constructor<?> firstConstructor = questionClass.getConstructor(String.class, String.class, String.class);

final Question firstQuestion = (Question) firstConstructor.newInstance("조앤", "제목1", "내용1");

assertThat(firstQuestion.getWriter()).isEqualTo("조앤");
assertThat(firstQuestion.getTitle()).isEqualTo("제목1");
assertThat(firstQuestion.getContents()).isEqualTo("내용1");


---

// Question의 생성자
public Question(String writer, String title, String contents) {
    this(0, writer, title, contents, new Date(), 0);
}

 

13. 이름을 이용해 Field 가져오기

final Class<?> questionClass = Question.class;
final Field field = questionClass.getDeclaredField("questionId");

assertThat(field.getName()).isEqualTo("questionId");

questionId이라는 이름을 가진 필드를 가져온다.

 

14. 이름을 이용해 가져온 Field의 리턴 타입 가져오기

final Field field = Question.class.getDeclaredField("questionId");
final Class<?> fieldClass = field.getType();

assertThat(fieldClass.getSimpleName()).isEqualTo("long");

 

15. field의 setter를 접근 가능하게 하고, 해당 필드를 변경한다.

final Class<?> studentClass = Student.class;
final Student student = new Student();
final Field field = studentClass.getDeclaredField("age");
field.trySetAccessible();

assertThat(field.getInt(student)).isZero();
assertThat(student.getAge()).isZero();

field.set(student, 99);

assertThat(field.getInt(student)).isEqualTo(99);
assertThat(student.getAge()).isEqualTo(99);