[Effective Java 3/e] 아이템 28 - 배열보다는 리스트를 사용하라


아이템 28 - 배열보다는 리스트를 사용하라

배열과 제네릭 타입의 차이

  • 배열은 공변이다.
    • Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다.(공변, 즉 함께 변한다)
  • 제네릭은 불공변이다.
    • Type1, Type2가 있을 때, List은 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이 발생하는 일을 막아주겠다는 제네릭 타입 시스템의 취지에 어긋남

리스트 기반 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()));
  }
}

정리

  • 배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입 정보가 소거된다.
  • 배열은 런타임에는 타입 안전하지만 컴파일에는 그렇지 않다. 제네릭은 반대다.