본 글은 김영한님의 스프링 핵심 원리 - 기본편
을 본 이후에 작성되었습니다.
오늘 강의를 들으면서 .class를 왜 사용하는걸까? 라는 의문이 들었다.
사실 안드로이드 코드를 짤 때 intent 이후 특정 Activity를 호출하기 위해 ::class.java
라는 코드를 썼었는 데, 그때는 왜 쓰는지 몰랐다.
이번에는 그냥 넘어가기 좀 그래서 알아보았다.
내가 궁금했던 부분은 @Configuration과 @Bean을 사용하여 Spring Container에 구현체를 Bean으로 등록한 이후 AnnotationConfigApplicationContext에 AppConfig.class를 넘겨줄 때 왜..? 라는 생각이 들었다.
코드는 다음과 같다.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext
코드를 보면 다음과 같이 되어 있다.
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
Class
라는 Arguement를 받고 있고, 이 componentClasses
를 register()라는 method로 등록하는 것을 확인할 수 있다.
그러면 .class 객체를 넘기는 데, instance를 넘기는 건지? 아니면 다른 특정값을 넘기는건지 생각이 든다.
아! 근데 확실한 것은 instance를 넘기진 않는다. 왜냐하면 new
키워드가 없기 때문이다. 그럼 AppConfig.class가 곧 Class Type이라는 건데, Class는 무엇인가?
Class
는 무엇인가?Class
는 다음과 같이 나타나 있다.
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement,
TypeDescriptor.OfField<Class<?>>,
Constable {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
private static native void registerNatives();
static {
registerNatives();
}
private Class(ClassLoader loader, Class<?> arrayComponentType) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
componentType = arrayComponentType;
}
- 코드를 보면 public 생성자가 없다. 다 private으로 생성된다. 그러면, 해당 객체의 생성은 당연히 JVM이 알아서 통제한다.
- 따라서 거의 모든 Java의 자료형(참조, 원시 포함)은 Class 객체로 표현할 수 있다.
그러면 .class를 쓰면 어떻게 바뀌는가?
.class(::class.java)
를 쓰면 어떻게 바뀌는가?
.class
는클래스 리터럴
을 나타내는 특별한 표기법이다. 이 클래스 리터럴은 class의 메타정보를 갖고 있는 데, class 리터럴을 통해 해당 클래스의 메타 정보를 갖고 올 수 있다.
무엇인지는 좀 더 봐야하겠지만, 아래의 코드를 보면 어느 정도 감이 온다.
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (loader == null) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (ccl != null) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
forName
이라는 method는 Class<?> 타입을 return하고 있다. 그러면 무엇을 리턴하는가?
어떤 클래스의 이름, 초기화, loader, caller를 담아 return하고 있다. 해당 정보가 바로 메타 정보로 사용되는듯 싶다.
java의 경우 .class
를 사용하나, kotlin의 경우 ::class.java
를 사용한다.
아니다. 생성되지 않는다. forName을 통해 해당 클래스의 메타정보만 갖고올 뿐 인스턴스가 생성되는 것은 아니다.
그러면 앞서 작성했던
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
는 kotlin에서 아래와 같이 사용하면 된다.
val applicationContext = AnnotationConfigApplicationContext(AppConfig::class.java)
결국 .class(::class.java)
를 사용하는 이유는 해당 class의 메타 정보를 얻기 위해서 사용된다. .getClass()
를 사용해도 같은 결과를 얻을 수 있으니, 참고하자.