문제 역사:
자바 제네릭스는 자바 5에서 컬렉션 및 메소드의 타입 안전성을 보장하기 위해 도입되었지만, 이전에 작성된 바이트코드와의 호환성을 유지하기 위해 구현되었습니다. 이를 위해 타입 지우기(type erasure) 메커니즘이 적용되었습니다.
문제:
자바 컴파일러는 엄격한 타입 규정을 요구하지만, 실행 중 JVM은 타입 매개변수에 대해 알지 못하며, 많은 오래된 클래스 및 라이브러리, 심지어 자바의 컬렉션 라이브러리도 일반적인 '원시' 타입(raw types)을 사용합니다. 기존 개발을 지원하기 위해서는 하위 호환성이 필요합니다.
해결책:
타입 지우기는 매개변수화된 타입(제네릭)을 '원시' 버전으로 변환하는 과정으로, JVM이 기존 바이트코드와 상호작용할 수 있도록 합니다. 타입 매개변수에 대한 모든 정보는 컴파일 단계에서 삭제되며, 대신 Object 타입(또는 명시된 경우 제한 조건)을 사용합니다.
코드 예시:
List<String> stringList = new ArrayList<>(); stringList.add("hello"); String s = stringList.get(0); // get은 Object를 반환하지만 컴파일러가 캐스트를 삽입합니다.
주요 특징:
List<String>, List<Integer> 등이 어떤 것인지 알 수 없습니다.제네릭 매개변수로만 메소드 오버로딩을 사용할 수 있나요?
아니요. 타입 지우기 때문에 컴파일러는 동일한 이름과 다른 제네릭 매개변수에 대해 동일하게 취급합니다. 예를 들어,
// 컴파일 오류! void process(List<String> list) { } void process(List<Integer> list) { }
제네릭 타입의 배열을 만들 수 있나요?
직접적으로는 불가능합니다. 타입 지우기가 JVM이 특정 제네릭 타입의 배열을 저장하는 것을 허용하지 않기 때문입니다. 예를 들어,
List<String>[] array = new List<String>[10]; // 컴파일 오류
원시 타입의 배열을 사용하여 우회 가능하지만 이는 안전하지 않습니다:
List<String>[] array = (List<String>[]) new List[10];
런타임에 instanceof를 통해 제네릭 타입을 확인할 수 있나요?
아니요, 타입 매개변수 정보가 삭제되기 때문에 가능합니다. 확인:
if (obj instanceof List<String>) { ... } // 컴파일 오류
더 적절한 방법은 기본 타입만 확인하는 것입니다:
if (obj instanceof List) { ... }
프로그래머가 다양한 매개변수를 저장하기 위한 매개변수화된 타입의 배열을 생성합니다. 결국 프로그램이 장시간 실행된 후 배열에서 객체를 추출할 때 ClassCastException이 발생합니다 - 런타임 타입 검사가 보장되지 않기 때문입니다.
장점:
단점:
배열 대신 컬렉션을 사용하고(예: List<List<String>>), 모든 타입 검사를 컴파일러에 위임합니다.
장점:
단점: