스터디/이펙티브 자바

[Effective Java] Item28. 배열보다는 리스트

📝 작성 : 2022.06.12  ⏱ 수정 : 
728x90

리스트가 사용하기 더 편한 것을 제외하고서라도 배열보다는 리스트의 사용을 권장하는 이유가 있습니다.

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

배열은 공변(covariant)

Sub가 Super의 하위 타입이라면 Sub[]는 Super[]의 하위 타입입니다. 이를 공변이라고 합니다. 반면 제네릭은 불공변(invariant, 무변성)입니다.

public class Application {
    public static void main(String[] args) {
        Object[] objArr = new Integer[1];
        List<Object> ol = new ArrayList<Integer>(); //컴파일 에러
    }
}

위 코드는 java: incompatible types: java.util.ArrayList<java.lang.Integer> cannot be converted to java.util.List<java.lang.Object>라는 컴파일 에러를 발생시킵니다.

근데 배열쪽에도 문제가 있습니다. objArr에 String 타입인 "100"을 넣는다면 컴파일은 되지만 런타임시 ArrayStoreException이 발생합니다.

결국 둘 다 Intger용 저장소에 String을 넣을 수는 없지만 배열은 런타임에야 알게 되지만, 리스트를 사용하면 컴파일 타임때 바로 알 수 있습니다.

배열은 실체화(reify)

배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인합니다. 그래서 위의 예에서 ArrayStoreException이 발생한 것입니다.
반면, 제네릭은 런타임에는 소거되어 담기로 한 원소 타입을 알 수 없습니다.(기존 코드와의 호환성때문에 이렇게 설계)

배열과 제네릭

위의 두 차이(공변, 실체화)로 인해 배열과 제네릭은 잘 어우러지지 못합니다.
배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없습니다. 즉, new List<e>[], new List<String>[], new E[]와 같이 작성하면 컴파일 타임에 제네릭 배열 생성 오류를 일으킵니다. 그래서 E, List<E>, List<String>같은 타입을 실체화 불가 타입(non-refiable type)이라고 합니다. 실체화되지 않기 때문에 런타임에는 컴파일 타임보다 타입 정보를 더 적게 가지는 타입입니다. 

 

이러한 소거 메커니즘 때문에 매개변수화 타입 가운데 실체화 될 수 있는 타입은 List<?>, Map<?, ?> 같은 비한정적 와일드카드 타입뿐입니다. 하지만 배열을 비한정적 와일드카드 타입으로 만들 수는 있지만 유용하게 쓰일 일은 거의 없습니다. 

 

 

반응형