Effective Java - Item 28

Updated:

Effective Java

Item 28. 배열보다는 리스트를 사용하라

배열과 제네릭 타입의 차이

  1. 배열은 공변이다.
    Sub이 Super의 하위 타입일 때, Sub[]은 Super[]의 하위 타입 하지만 제네릭은 List와 List는 아무 차이가 없다.
    List<Object> objects = new ArrayList<String>();
    objects.add(12); // 컴파일 단계에서 오류를 체크한다.
    
  2. 배열은 실체화 한다. 컴파일 이후 런타임에도, 자신의 원소의 타입을 인지하고 확인한다. 제네릭은 확인하지 않음. => 제네릭은 타입 정보가 런타임에서 소거 된다.
    따라서 제네릭은 실체화 불가 타입 이다.

  3. 제네릭 배열은 만들 수 없다.
    List<String>[] stringList = new List<String>()[10]; // 사용 지양
    

    런타임에 ClassCastException 발생을 막아준다는 제네릭 타입 시스템 취지에 어긋난다.

배열 대신 제네릭으로 사용하기

// 코드 28-6 리스트 기반 Chooser - 타입 안전성 확보! (168쪽)
public class Chooser<T> {
    private final List<T> choiceList;       // 중요, 원래는 Object[]

    public Chooser(Collection<T> choices) {         // Collection으로 받는다
        choiceList = new ArrayList<>(choices);      // 중요, 원래는 choices.toArray()
    }

    public T choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceList.get(rnd.nextInt(choiceList.size()));
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6);

        Chooser<Integer> chooser = new Chooser<>(intList);
        String choice = chooser.choose();

        Set<String> stringSet = new HashSet<>();
		stringSet.add("hi");
		stringSet.add("bye");
		stringSet.add("see");
		
		Chooser<String> chooser2 = new Chooser<>(stringSet);       // Set을 ArrayList로 변환
		System.out.println(chooser2.choose());
    }
}


위 사진과 같이 Collection의 하위 타입이면, ArrayList로 변환하여 랜덤 값을 뽑는다.

결론

배열과 제네릭에는 매우 다른 타입 규칙이 적용된다.
배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입정보가 소거된다.
배열은 런타임에는 타입 안전한 반면, 컴파일 타임에는 그렇지 않다. 제네릭은 그 반대다. 따라서 둘을 섞는 것은 지양하며, 섞어 쓰다가 컴파일 오류나 경고를 만나면 배열을 리스트로 대체하자.

출처

Joshua Bloch. Effective Java 3/E. n.p.: 인사이트, 2018년 11월 1일.

Reference

https://sejoung.github.io/2018/12/2018-12-27-Prefer_lists_to_arrays/#%EC%95%84%EC%9D%B4%ED%85%9C-28-%EB%B0%B0%EC%97%B4%EB%B3%B4%EB%8B%A8-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC