0. 리플렉션
리플렉션은 Class API를 사용한다.
Class API 에 있는 함수들을 통해서 클래스의
- Field 의 값, 혹은 이름
- 상위 클래스
- 인터페이스
- 메소드 목록
- 어노테이션 등 클래스에 대한 모든 정보에 접근할 수 있다.
왜?
- JVM ClassLoader 가 로딩이 끝난 뒤에, Heap에 Class 타입에 대한(Class<MyClass>) Class 정보를 등록했기 때문이다.
- Class 정보 가져오는 법
- Book 이라는 클래스가 있는 경우, 아래와 같은 3가지 방법으로 Class 정보를 가져올 수 있다.
// .class 인스턴스를 이용해서 가져오는 방법
Class<Book> bookClass = Book.class
// 객체에 대한 인스턴스를 생성한 이후, .getClass() 로 가져오는 방법
Book book = new Book();
Class<? extends Book> bookClass2 = book.getClass();
// FQCN 을 이용해서 가져오는 방법
Class<?> bookClass3 = Class.forName("me.sjlee.Book");
1. 어노테이션과 리플렉션
1. Retention()
- 바이트코드를 로딩했을 때 (Method 영역에 메모리상으로 올렸을 때) 메모리상에는 저장되지 않는다.
- 바이트코드에는 남아있으나, 로딩 이후에 메모리에는 올라가지 않는다.
- default = [@Retention(RetentionPolicy.CLASS]
- 메모리상에 남게하고 싶다면 @Retention(RetentionPolicy.RUNTIME) 을 사용한다.
- 바이트코드에는 남아있으나, 로딩 이후에 메모리에는 올라가지 않는다.
2. Target()
- 어노테이션을 사용할 위치를 제한하고 싶다면 @Target() 을 사용한다.
3. 어노테이션 필드 타입
- 어노테이션은 값을 제한된 타입만 가질 수 있다.
- primitive
- String
- Enum
- another Annotation
- Class
- 위의 5개의 배열 형식
- 필드 사용
- String name(); / @MyAnnotation(name="leesukjune")
- 기본값 설정법
- String name() default "leesukjune";
- value 키워드
- String value(); / @MyAnnotation("value 일때는 그냥 써도 된다.")
4. Interited()
- 상위 클래스에서 상속받은 어노테이션을 같이 사용하기 위해 사용
Class 정보를 통해 조회
MyClass.class.getAnnotations();
MyClass.class.getDeclaredAnnotations();
2. 클래스 정보 수정
이러한 정보들을 이용해서 생성자 생성 / 필드값 변경 및 가져오기 / Method 실행하기 등을 할 수 있다.
- Class.getConstructor
- Class.getField
- Class.getMethod
Book
public class Book {
private int a;
public Book() {
}
private int sum(int a, int b) {
return a + b;
}
}
예시) 객체 생성
// Class 타입의 클래스 정보 가져오기
Class<?> bookClass = Class.forName("demo.reflection.Book");
// NoArgsConstructor 인 경우 생성하기
Constructor<?> constructor = bookClass.getConstructor(null);
Book book = (Book) constructor.newInstance();
// AllArgsConstructor 인 경우 생성하기
Constructor<?> allArgsConstructor = bookClass.getConstructor(int.class);
Book book2 = (Book) allArgsConstructor.newInstance(123);
System.out.println("is instance? : " + (book instanceof Book));
예시) 필드 값 가져오기
Book book = new Book();
Field a = Book.class.getDeclaredField("a");
// private field 인 경우 아래처럼 접근 가능할 수 있도록 해줘야 함
a.setAccessible(true);
// 값을 가져오기 위해서 인스턴스가 필요함
System.out.println(a.get(book));
예시) 메서드 수행하기
Method method = Book.class.getDeclaredMethod("method", int.class, int.class);
method.setAccessible(true);
int invoked = (int) method.invoke(new Book(), 1, 2);
System.out.println(invoked);