본문 바로가기

Dev Book Review/Effective Java

[Effective Java] item28. 배열보다는 리스트를 사용하라

1. 배열과 제네릭의 차이

배열

  • 공변 (convariant) - SubSuper의 하위타입이라면 배열 Sub[]는 배열 Super[]의 하위타입이다. (함께 변한다)
  • 배열에서는 실수를 런타임에 타입 오류를 알 수 있다
  Object[] objectArray = new Long[1];
  objectArray[0] = "타입이 달라 넣을 수 없다" // ArrayStoreException

실체화(reify)된다 : 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.

제네릭

  • 불공변 (invariant) - 서로 다른 타입 Type1,Type2가 있을 때 List<Type1>List<Type2> 의 하위타입도 아니고 상위타입도 아니다
  • 리스트에서는 컴파일할 때 타입 오류를 바로 알 수 있다.
  List<Object> ol = new ArrayList<Long>();    // 호환되지 않는 타입이다.
  ol.add("타입이 달라 넣을 수 없다.")

소거(erasure)된다 : 원소 타입을 컴파일 타임에만 검사하며 런타임에는 알 수 없다.
제네릭 지원 전과 제네릭 타입을 함게 사용할 수 있게 해주는 메커니즘이다.

 

2. 제네릭 배열은 사용불가하다.

배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.
new List<E>[], new List<String>[], new E[] : 컴파일시 제네릭 생성오류를 일으킨다.

이유 : 타입안전하지 않기 때문이다
컴파일러가 자동 생성한 형변환 코드에서 런타임에 ClassCastException이 발생할 수 있다 : 제네릭 타입의 취지와 맞지 않다.

 

3. 실체화 불가 타입

E, List<E>, List<String> : 실체화 불가 타입

실체화 되지 않아 런타임에는 컴파일 타임보다 타입점보를 적게 가지는 타입

List<?>, Map<?,?> : 소거 메커니즘 때문에 매개변수화 타입 가운데 실체화 가능한 타입 - 비한정적 와일드카드 타입

 

4. 배열의 불편함

a. 제네릭 컬렉션에서는 자신의 원소 타입을 담은 배열을 반환하는게 불가능하다.

아이템 33의 타입 안전 이종 컨테이너를 이용하여 자신의 원소타입을 추론할 수있다 (우회)

b. 제네릭 타입과 가변인수 메서드를 함께 쓰면 해석하기 어려운 경고 메세지를 받게된다.

가변인수 메서드를 호출할 때마다. 가변인수를 담는 배열이 만들어지는데, 이때 그 배열의 원소가 실체화 불가 타입이면 경고가 뜬다. @SafeVarargs로 해결한다.

 

5. 배열대신 리스트를 사용하자

  • 장점 : 타입 안정성과 상호 운용성이 좋아진다.
  • 단점 : 코드가 조금 복잡해지고 성능이 살짝 나빠질 수도 있다.
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.netInt(choiceList.size()));
  }
}

List를 사용하면 런타임에 ClassCastException을 만날 일은 없다.

public class Chooser<T>{
  private final T[] choiceArray;

  public Chooser(Collection<T> choices){
    choicesArra = (T[]) choices.toArray; 
  }

    public Object choose(){
    Random rnd = ThreadLocalRandom.current();
    return choiceArray[rnd.nextInt(choiceArray.length)];
  }
}

T[] 로의 타입캐스팅 과정에서 경고가 뜬다. 제네릭에서는 원소의 타입정보가 소거되어 런타임에는 타임 정보를 알 수 없기 때문이다. item 27 비검사 경고를 제거하라는 말에 따라 위험요소를 제거할 수 있는 최선의 방법은 List로 구현하는 것이었다.