[Effective Java 3/e] 아이템 28 - 배열보다는 리스트를 사용하라
아이템 28 - 배열보다는 리스트를 사용하라
배열과 제네릭 타입의 차이
- 배열은 공변이다.
- Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다.(공변, 즉 함께 변한다)
- 제네릭은 불공변이다.
- Type1, Type2가 있을 때, List
은 List 의 하위 타입도 아니고 상위 타입도 아니다.
- Type1, Type2가 있을 때, List
런타임에 실패한다
Object[] objectArray = new Long[1];
objectArray[0] = "타입이 달라 넣을 수 없다."; // ArrayStoreException을 던진다.
컴파일되지 않는다
List<Object> ol = new ArrayList<Long>(); // 호환되지 않는 타입이다.
ol.add("타입이 달라 넣을 수 없다.");
- 배열은 실체화 된다.
- 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.
- 제네릭은 타입 정보가 소거(erasure)된다.
- 원소 타입을 컴파일타임에만 검사하며 런타임에는 알 수 없다.
- 배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.
- new List
[], new List [], new E[] -> 컴파일할 때 제네릭 배열 생성 오류 발생 - 제네릭 배열을 만들지 못하게 막은 이유
- 타입 안전하지 않기 때문
- 허용 시 컴파일러가 자동 생성한 형변환 코드에서 런타임에 ClassCastException이 발생할 수 있다.
- 런타임에 ClassCastException이 발생하는 일을 막아주겠다는 제네릭 타입 시스템의 취지에 어긋남
- new List
리스트 기반 Chooser - 타입 안전성 확보
public class Chooser<T> {
private final List<T> choiceList;
public Chooser(Collection<T> choices) {
choiceList = new ArrayLIst<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
}
정리
- 배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입 정보가 소거된다.
- 배열은 런타임에는 타입 안전하지만 컴파일에는 그렇지 않다. 제네릭은 반대다.