java.lang.Class
클래스는 Java에서 제공하는 reflection 기능들을 사용하기 위한 entry point입니다
해당 클래스 타입의 객체를 얻을 수 있는 방법은 크게 두 가지가 있습니다
Object.getClass()
메소드 호출.class
문법 사용두 방법 모두 java.lang.Class
타입의 객체를 반환합니다
특정 클래스와 인스턴스에 대해 두 방법을 사용해 Class
타입 객체를 얻은 후 비교하면 동일하다는 결과를 확인할 수 있습니다
@Test
void test() {
String s = "I am Groot";
Class fromObject = s.getClass();
Class fromClass = String.class;
assertThat(fromObject).isEqualTo(fromClass); // Pass
}
상속 관계에 있는 두 클래스를 선언하고 다음과 같은 테스트 코드를 실행했습니다
public class Person {
protected String name;
}
public class Gongmeda extends Person {
}
@Test
void testWithInheritance() {
Person person = new Gongmeda();
Class fromObject = person.getClass();
Class fromClass = Person.class;
assertThat(fromObject).isEqualTo(fromClass); // Fail
}
코드에서 변수 person
은 Gongmeda
클래스의 인스턴스를 가르키고 있지만 부모 클래스인 Person
으로 선언되었습니다
따라서 person.getClass()
메소드의 결과 또한 Person
클래스일 것 같지만 메소드는 Gongmeda
클래스를 반환합니다
왜냐하면 변수 person
은 runtime에 Gongmeda
클래스 타입의 인스턴스를 가르키고 있으며, Object.getClass()
메소드는 runtime 기준의 타입을 반환하는 메소드이기 때문입니다
반면, .class
는 항상 해당 클래스에 대해 동일한(static한) 타입을 반환합니다
Object.getClass()
는 Object
의 하위 타입들이 사용할 수 있는 메소드입니다
따라서 int
와 같은 원시 타입에 대해 사용할 경우 컴파일 오류가 납니다
error: int cannot be dereferenced
Class numClass = num.getClass();
^
반면, .class
문법을 원시 타입에 사용할 경우, 성공적으로 Class
객체를 반환합니다
@Test
void testWithPrimitiveType() {
int num = 10;
Class numClass = int.class; // Compile Success
assertThat(numClass.getName()).isEqualTo("int"); // Pass
}
package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
public class Object {
// ...
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
// ...
}
위에서 확인할 수 있듯이 Object.getClass()
는 Object
클래스에 존재하는 메소드입니다
따라서 Object
타입의 인스턴스에서만 사용할 수 있기 때문에 인스턴스를 만들지 않고 클래스에서는 직접 호출할 수 없습니다
이러한 경우에는 .class
문법을 사용해서 클래스에서 직접 static하게 Class
객체를 얻어야 합니다