배열보다 리스트

Sub가 Super의 하위 타입이면 Sub[]는 Super[]의 하위 타입이 된다. 배열은 공변(covariant)이기 떄문이다. 그러나 제네릭은 불공변(invariant)다. 보통 제네릭이 문제가 있다고 생각할 수 있다.

Object[] arr = new Long[3]은 컴파일은 된다. 런타임에 ArrayStoreException을 낸다. 그러나 List<Object> list = new ArrayList<Long>()은 컴파일조차 되지 않는다. 즉, 제네릭이 정상이고 배열쪽이 비정상이라는 소리다.

그럼 제네릭으로 배열을 만들면 되지 않을까? 일단 정상적인 방법으로는 허용하지는 않는다. 배열은 컴파일에 체크 되지만 실질적으로 런타임에 저장 가능한지를 체크한다. 그래서 위와 같은 경우가 허용되지 않는다. 즉, 실체화가 된다. 그러나 제네릭은 런타임에 타입이 소거된다. 즉 컴파일 타임에만 강하게 검사한다. 이는 런타임에는 타입을 알 수 없음을 의미한다. 즉, type-safe하지 않기에 허용하지 않는다. 제네릭은 실체화 불가 타입이라고 한다. 실체화되지 않아서 오히려 런타임에는 컴파일보다 타입 정보를 적게 가진다.

위에서 제네릭 배열은 ‘정상적인 방법’에서는 컴파일 타임에 에러를 낸다고 했다. 가능은 하다. 가변인자로 제네릭을 받을 수 있다. 그러나 난해한 경고 메시지를 뱉는다. 물론 @SafeVarargs로 대처할 수 있다. 이는 해당 어노테이션과 함께 ‘왜 무시할 수 있는지?’ 이유를 주석으로 남겨야함을 의미한다. 또한 Object[]을 만들고 제네릭으로 형변환을 할 수도 있다. 이래도 경고를 뱉는다.

차라리 List<E>를 사용하는 편이 낫다.