제네릭 클래스
기존의 클래스와 유사하지만, 클래스 이름 다음에 일반화된 타입(Generic Type)의 매개 변수를 “<>” 다이아몬드 연산자에 추가한다는 차이가 있다.
public class MyClass<T> { // 제네릭 클래스 MyClass, 타입 매개 변수 T
T val; // 변수 val 타입은 T로 지정한다.
void set(T a) {
val = a; // T 타입의 a 값을 val에 지정한다.
}
T get() {
return val; // T 타입의 val 값을 반환한다.
}
}
구체화
제네릭 클래스나 인터페이스를 사용할 때, 그 안에 정의된 타입 매개 변수에 실제로 사용할 구체적인 타입 인자를 지정하여 객체를 만드는 과정이다.
- 타입 매개 변수: 제네릭 정의 시 사용되는 임시 이름이다.
- 구체적인 타입 인자: 실제로 지정하는 타입이다.
// 제네릭 클래스 정의
class Box<T> { /* ... */ }
// 구체화 과정
Box<String> stringBox = new Box<String>(); // T를 String으로 구체화한다.
Box<Integer> integerBox = new Box<>(); // T를 Integer로 구체화한다. (다이아몬드 연산자 사용)
타입 소거
컴파일러가 제네릭 코드를 바이트 코드로 변환할 때, 모든 타입 매개 변수를 가장 기본적인 타입으로 변환하거나 제거하는 것으로, 자바 컴파일러가 구체화 과정을 처리할 때 사용하는 방식이다.
| 제네릭 타입 정의 | 바이트 코드 변환 후(소거) |
|---|---|
List<String> |
List<Object> |
| T(제한이 없는 타입) | Object |
| T extends Number(제한된 타입) | Number |
- 컴파일 타임에 타입 안정성 제공: 자바의 제네릭은 컴파일 타임에만 타입 안정성을 제공하며, 런타임에는 구체적인 타입 정보가 존재하지 않는다.
- 역호환성: 자바에서는 이전 버전과의 호환성을 유지하기 위해 타임 소거를 사용하며, (Java 5 이후부터)기존에 수많은 자바 라이브러리와 애플리케이션은 제네릭 없이 이미 컴파일되어 있었기에 이를 호환하기 위해 채택했다.
타입 매개 변수
제네릭 타입을 가진 객체의 생성이 허용되지 않으며, 예제 코드는 다음과 같다.
// 컴파일러가 E에 대한 구체적인 타입을 알 수 없어 호출된 생성자를 결정할 수 없고,
// 객체 생성 시 어떤 크기의 메모리를 할당해야 할 지 전혀 알 수 없다.
public class MyVector<E> {
E create() {
E a = new E();
return a;
}
}
배열
제네릭 클래스 또는 인터페이스 타입의 배열은 선언할 수 없다.
GStack<Integer>[] gs = new GStack<Integer>[10]; // 컴파일 오류