Java에서 java.lang.Class는 모든 reflection 작업(JVM에 로딩되어 있는 클래스와 메소드 정보를 읽어 오는 것)의 시작점이다. java.lang.Class의 객체를 가지고 있다면 reflection class의 객체를 얻기위해 해당되는 메서드를 불러올 수 있다.
Object.getClass() 메서드는 Object class의 인스턴스 메서드이다. Object를 갖고 있다면 그 타입의 Class object를 가질 수 있다.
유사하게 우리는 Class object를 얻기 위해 ClassName.class를 사용할 수 있다. 아래 예시가 분명히 보여준다.
@Test
public void givenObjectAndType_whenGettingClassObject_thenTwoMethodsHaveTheSameResult() {
String str = "I am an object of the String class";
Class fromStrObject = str.getClass();
Class clazz = String.class;
assertSame(fromStrObject, clazz);
}
위의 test 메서드에서 언급한 두가지 방법을 사용해서 String의 Class object를 얻으려 했고 assertion methond는 두 개의 Class object가 같은 인스턴스를 가지고 있다는 것을 알려주었다.
그러나 두 가지 접근에는 차이가 있다. 더 자세히 살펴보자.
위의 예시에서 str.getClass() 메서드를 부를 땐 str object의 runtime type 을 얻었고 String.class는 String class를 static하게 생각했다. 위의 예시에서는 runtime type과 String.class는 같았다.
하지만 클래스 상속이 섞인다면 다를 수 있다. 두 가지 클래스를 보자.
public class Animal {
protected int numberOfEyes;
}
public class Monkey extends Animal {
// monkey stuff
}
@Test
public void givenClassInheritance_whenGettingRuntimeTypeAndStaticType_thenGetDifferentResult() {
Animal animal = new Monkey();
Class runtimeType = animal.getClass();
Class staticType = Animal.class;
assertSame(staticType, runtimeType);
}
위의 test를 실행하면 test는 실패한다.
Test 메서드에서 Monkey animal = new Monkey();가 아니라 Animal animal = new Monkey();로 선언했음에도 animal 객체의 runtime type은 Monkey이다. 왜냐하면 animal 객체는 runtime시에 Monkey의 인스턴스이기 때문이다.
하지만 Animal class의 static type을 얻을 때는 type은 항상 Animal이다.
Java code를 쓸 때 primitive type을 자주 사용한다. object.getClass()를 이용해서 primitive type의 Class object를 얻어보자.
int number = 7;
Class numberClass = number.getClass();
위 코드를 컴파일 하면 컴파일 에러가 나온다.
Error: java: int cannot be dereferenced
컴파일러는 number 변수를 dereference(포인터가 가르키는 번지에 수납된 데이터에의 접근)할 수 없다. primitive type 변수이기 때문이다. 그러므로 object.getClass() 메서드는 primitive type의 Class object를 얻을 수 없다.
.class 를 이용해 primitive type을 얻어보자.
@Test
public void givenPrimitiveType_whenGettingClassObject_thenOnlyStaticTypeWorks() {
Class intType = int.class;
assertNotNull(intType);
assertEquals("int", intType.getName());
assertTrue(intType.isPrimitive());
}
int.class를 통해 int primitive type의 Class object를 얻을 수 있다. Java 9 또는 그 이후에 버전에서 primitive type의 Class object는 java.base 모듈에 속해있다.
위의 테스트가 보여주듯 .class는 primitive type의 Class object를 얻는 쉬운 방법이다.
object.getClass() method가 runtime type의 class object를 가져다 준다는 것을 알았다.
Class object의 type을 얻기를 원한다고 생각해보자 abstract class, interface 혹은 인스턴스 생성을 허용하지 않는 클래스라서 instance는 얻을 수 없다.
public abstract class SomeAbstractClass {
// ...
}
interface SomeInterface {
// some methods ...
}
public class SomeUtils {
private SomeUtils() {
throw new RuntimeException("This Util class is not allowed to be instantiated!");
}
// some public static methods...
}
이런 상황에서느 object.getClass() method를 이용해서 Class object를 얻을 수 없다. 하지만 .class는 가능하다.
@Test
public void givenTypeCannotInstantiate_whenGetTypeStatically_thenGetTypesSuccefully() {
Class interfaceType = SomeInterface.class;
Class abstractClassType = SomeAbstractClass.class;
Class utilClassType = SomeUtils.class;
assertNotNull(interfaceType);
assertTrue(interfaceType.isInterface());
assertEquals("SomeInterface", interfaceType.getSimpleName());
assertNotNull(abstractClassType);
assertEquals("SomeAbstractClass", abstractClassType.getSimpleName());
assertNotNull(utilClassType);
assertEquals("SomeUtils", utilClassType.getSimpleName());
}
Class object를 얻으려하는데 instance를 못 얻을 때는 .class 가 방법이다.
| object.getClass() | SomeClass.class | |
|---|---|---|
| Class objects | The runtime type of object | The static Type of SomeClass |
| Primitive types | - | Works staightforwadly |
| Interfaces, abstract classes, or classes that can't be instantiated | - | Works staightforwadly |