// Object.java
public final native Class<?> getClass();
Object 클래스편 마지막으로 getClass()를 살펴보자.
Object 클래스 코드를 살펴보면 리턴타입이 Class 이다.
Class 클래스가 무엇일까?
Class 클래스
는 클래스와 인터페이스(Class, Interface, Enum, Annotation...)의 모든 정보를 담고있는 클래스이다.
클래스 당 1개만 존재하며, 클래스 로더에 의해서 메모리에 올라갈 때 힙 영역에 자동으로 생성된다.
바로 코드로 이해해보자.
public class Book implements Readable {
private String name;
private int price;
private int stock;
public static final int OFF = 30;
public Book(String name, int price) {
this.name = name;
this.price = price;
}
private boolean isSoldOut() {
return this.stock < 0;
}
// getter, setter
...
}
public static void main(String[] args) throws Exception {
Book book = new Book("SPRING BOOK", 10000);
Class<? extends Book> bookClass = book.getClass();
}
간단하게 Book 클래스를 만들고, Object 클래스에서 상속받은 getClass() 를 호출했다.
위의 설명에서 Class 클래스는 클래스의 정보를 담고 있는 클래스라고 했다.
그럼 Class 클래스로 어떤 일을 할 수 있는걸까 생각해보자.
모두 가능하다!
왜? Java Reflection API 기법을 사용하기 때문에 가능하다.
Reflection 이란?
구체적인 클래스 타입을 알지 못해도 그 클래스의 정보(메소드, 타입, 변수, ...)에
접근할 수 있게 해주는 자바 API 기법
Class<T>를 가지고 할 수 있는 것들은 다음과 같다.
예제를 통해 가볍게 Reflection을 사용해보자.
// 1. 클래스 리터럴(*.class)로 얻기
Class<Book> bookClass = Book.class;
// 2. 생성된 객체로 얻기
Book book = new Book("SPRING BOOK", 10000);
Class<? extends Book> bookClass2 = book.getClass();
// 3. 클래스 이름(FQCN)으로 얻기
Class<?> bookClass3 = Class.forName("com.jihan.javastudycode.lang.Book");
Arrays.stream(bookClass.getDeclaredFields()).forEach(f -> {
try {
f.setAccessible(true); // true로 지정해야 private 접근가능
System.out.printf("%s %s \n", f, f.get(book));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
private java.lang.String com.jihan.javastudycode.lang.Book.name SPRING BOOK
private int com.jihan.javastudycode.lang.Book.price 10000
private int com.jihan.javastudycode.lang.Book.stock 0
public static final int com.jihan.javastudycode.lang.Book.OFF 30
getFields()를 할 경우 private 접근 제어자는 보이지 않음.
Arrays.stream(bookClass.getDeclaredMethods()).forEach(System.out::println);
public java.lang.String com.jihan.javastudycode.lang.Book.getName()
public java.lang.String com.jihan.javastudycode.lang.Book.toString()
public void com.jihan.javastudycode.lang.Book.setName(java.lang.String)
public int com.jihan.javastudycode.lang.Book.getPrice()
public void com.jihan.javastudycode.lang.Book.setPrice(int)
public int com.jihan.javastudycode.lang.Book.getStock()
public void com.jihan.javastudycode.lang.Book.setStock(int)
private boolean com.jihan.javastudycode.lang.Book.isSoldOut()
getMethods()를 할 경우 private 접근 제어자는 보이지 않음.
getMethods()를 할 경우 상속받은 메소드도 보여짐.
System.out.println(bookClass.getSuperclass());
class java.lang.Object
Arrays.stream(bookClass.getInterfaces()).forEach(System.out::println);
interface com.jihan.javastudycode.lang.Readable
Constructor<Book> constructor = bookClass.getConstructor(String.class, int.class);
Book theLordOfTheRings = constructor.newInstance("반지의 제왕", 20000);
그런데 특별한 경우가 아닌 이상 애플리케이션 개발 시 Reflection을 자주 사용하는 일은 없을 것이다.
그렇다면 Reflection 기술은 주로 어디서 활용되고 있는걸까?
다양한 프레임워크나 라이브러리 개발환경에서 많이 사용되는 기술이다.
또한 사용시 주의할 점이 몇 가지 있는데 강력한 기능인만큼 주의해서 쓰지 않으면 아래와 같이 문제가 발생할 수 있다.
https://docs.oracle.com/javase/tutorial/reflect/
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html
https://www.geeksforgeeks.org/reflection-in-java/
https://woowacourse.github.io/javable/post/2020-07-16-reflection-api/
https://www.inflearn.com/course/the-java-code-manipulation