Class 클래스를 찾아 공식 Java 도큐먼트에 들어갔더니 Class<T>라고 소개되고 있다. 그냥 Class 클래스에 대한 페이지도 있나 찾아봤는데 없다. 다들 Class라고 소개하는 클래스는 사실 Class<T>였던 것인가..!
Class 클래스에 대하여
Class 클래스가 왜 필요한가?
내가 어떤 car1이라는 이름의 객체를 생성해서 사용하고 있는데, 얘가 속한 클래스의 이름이 뭔지 궁금할 때 쓰인다.
클래스를 동적으로 로딩하거나 디컴파일할 때 쓰이게 된다.
Class 클래스의 소속
java.lang.Object
java.lang.Class<T>
위계가 이렇게 되어 있다. 즉, Object을 상속받고 lang 패키지에 속함.
Class 클래스는 추상 클래스인가?
아니다.
Class는 생성자를 감추고 있어서 new 연산자로 객체를 직접 만들어낼 수 없다. 이 특징이 추상 클래스같다는 생각이 들게 하지만, 다음의 2가지 이유로 아니다:
- 추상 클래스는 아예 인스턴스를 만들어 낼 수 없고 상속만 가능한 애들이다. Class는 인스턴스를 만들어내는 두 가지 방법이 있다.
- Class는 public final class Class<T> 라고 선언되어 있다. 추상 클래스의 선언 형식인 abstract class Box { … } 와 다르다.
그래서 Class 클래스란?
: 클래스와 인터페이스의 메타 데이터(클래스의 이름, 생성자/필드/메소드의 정보)를 관리하는 클래스를 말한다.
먼저 1) 알고 싶은 애(클래스)를 Class 인스턴스에 담아내고, 2) 만들어진 Class 인스턴스를 이용해 알고 싶은 정보를 빼내오는 형식으로 쓰면 된다.
// Class 인스턴스를 만드는 방법1: 만들어진 인스턴스가 이미 있는 경우.
Box box = new Box();
Class c = box.getClass();
// Class 인스턴스를 만드는 방법2: 만들어진 인스턴스가 없고 대신 패키지를 포함한 정확한 클래스명을 아는 경우.
Class c = Class.forName("패키지.클래스명");
클래스를 생물에 비유하자면,
1번 방식은 홍길동이라는 인간이 있을 때 거기서 인간의 DNA 설계도를 역추적하는 거고,
2번 방식은 DNA 도서관에서 “인간”이라는 이름으로 DNA 설계도를 찾는 것이다.
만들어진 Class 인스턴스 활용 간단한 예:
Class c = box.getClass();
c.getName();
c.getSimpleName();
c.getPackage().getName();
리플렉션에 사용되는 예:
Class c = box.getClass();
Constructor[] cons = c.getDeclaredConstructors();
Field[] fields = c.getDeclaredFields();
Method[] methods = c.getDeclaredMethods();
// 각각 생성자, 필드, 메소드를 배열로 반환받는다.
리플렉션이란:
클래스의 생성자, 필드, 메소드 정보를 알아내는 것.
→ Class 클래스가 리플렉션을 위해 제공하는 메소드에는 getDeclared… 시리즈 외에도 getFields(), getMethods()가 있다. 상속받은 멤버까지 불러오고 싶을 때 사용한다.
Class.forName() 방식은 런타임 로딩이다
로드타임 동적 로딩: 하나의 클래스를 로딩하는 과정에서 필요한 다른 클래스들을 또 로딩해오는 것.
런타임 동적 로딩: (지금 실행한) 클래스를 로딩할 때 어떤 클래스도(뭐가 올지 알지 못해서) 읽어오지 않고 안의 코드가 실행되는 순간에 필요한 클래스가 로딩되는 것.
동적 객체 생성
: 런타임 시에 인스턴스를 만들어 내는 것을 뜻한다.
런타임 동적 로딩 방식과 짝지어져 사용된다. 즉, 코드를 작성할 때 클래스 이름을 알 수 없고 런타임 시에 클래스 이름(=타입)이 확정되게 되는 경우, 그 클래스의 객체를 만들어야 하는 경우 똑같이 동적으로 생성하는 것이다. new 연산자를 사용하지 않는다.
Class.forName() 메소드로 Class 객체를 얻은 다음, newInstance() 메소드를 호출하면 해당하는 인스턴스를 만들어낼 수 있다.
try {
Class c = Class.forName("런타임 시 결정되는 클래스 이름"); // 만약 Box라면,
결정된_클래스_타입 new_box = c.newInstance();
} catch { ... }
Class literal에 대하여
Class<T> 도큐먼트의 개괄 말미에 이런 표현이 있다.
box.getClass().getName();
Box.class.getName();
// box 아니고 Box인 것에 주의.
DNA 도서관에서 “인간”의 DNA를 찾는 방식인 2번 방식을, class literal을 이용해서 오른쪽과 같이 써도 무방하다고. 참고하라는 *15.8.2 of The Java™ Language Specification* 에서는 별달리 도움 되는 내용을 찾지 못하고, The Java Tutorials - Class Literals as Runtime-Type Tokens 에서 약간의 유용한 내용을 찾았다:
- Java 5부터 제네릭이 도입되고, 그로 인해 Class가 Class<T>로 바뀐 것이었음.
- String.class의 타입은 Class<String>이다. 즉, .class를 하면 String클래스에 대한 정보를 담고 있는 Class(타입)이 된다.
- Class.newInstance() 메소드가 정확히 T 타입을 반환하게 되어서 활용시에 더 견고한 타입 제어를 할 수 있게 되었다.
- class literal을 가장 잘 쓰는 방법은 런타임 타입 토큰으로 쓰는 것이다.
그래서 Class literal이란
: 클래스와 인터페이스와 등등의 자료형들에 대한 정보를 담은 클래스 Class 인스턴스를 만드는 것. 클래스.class 와 같이 .class를 붙여서 Class<해당 클래스> 타입으로 만들어버리는 것이다. 그렇게 해놓고 Class 클래스의 메소드를 이용해 정보를 캐내는 거지. 자바에 존재하는 거의 모든 자료형에 대해 가능하다고 한다.
구체적으로는,
- 클래스.class, 인터페이스.class, 배열.class ⇒ Class<클래스/인터페이스/배열>
- 원시타입.class ⇒ Class<해당 래퍼클래스>
- void.class ⇒ Class<void>
이렇게 변환된다.
가장 도움되는 class literal 참고 문서:
구글링 끝에 또다른 참고 문서를 찾았다. (눈물이…)
The Java™ Tutorials - Retrieving Class Objects(Class 인스턴스를 얻어내는 방법) 이라는 페이지에 .class의 용법이 가장 명시적으로 나와있다. (공식 문서라랍시고 레퍼런스된 저 위의 두 개 문서보다 설명의 간결함과 명확성이 백배는 낫다.)
클래스의 인스턴스가 만들어지지 않은 경우, 마치 클래스 변수처럼 그냥 클래스(타입).class 해서 거기에 해당하는 Class<클래스(타입)> 인스턴스를 얻는 것이다!
예를 들면,
Class c = boolean.class; // Boxing conversion에 의해 자동으로 래퍼클래스로 변환됨.
Class c = java.io.PrintStream.class;
Class c = int[][][].class;
String.class.getName() => "java.lang.String"
byte.class.getName() => "byte"
이런 식으로 쓰면 된다.
아 그래서 인터페이스.class도 가능한거였구만? 그 인터페이스가 “무슨 (추상)메소드를 가지고 있는지에 대한 정보를 담은” Class 인스턴스가 생성되는 거니까. 인터페이스 자체를 인스턴스화하는 게 아니니까.
결론
class literal은 Class<> 인스턴스를 얻어내는 가장 간단한 하나의 방법이다.
주 참고문헌:
자바 튜토리얼 https://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
자바 도큐먼트 - Class<T> https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#newInstance--
자바 Language Specification https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.8.2
Kephi Javatory의 포스팅 https://kephilab.tistory.com/97