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

김종준·2023년 5월 28일
0

이펙티브자바

목록 보기
23/63

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

배열과 제네릭 타입에는 중요한 차이가 2가지 있다.

첫 번째, 배열은 공변이다.

예를 들면 Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다.

반면, 제네릭은 불공변이다.

즉, 서로 다른 타입 Type1과 Type2가 있을 때, List<Type1>List<Type2>하위 타입도 아니고 상위 타입도 아니다.

이는 배열은 실수를 런타임에서야 알게 되지만, 리스트를 사용하면 컴파일할 때 바로 알 수 있는 차이를 만든다.

두 번째 차이로, 배열은 실체화된다.

배열런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.

하지만 제네릭은 타입 정보가 런타임에는 소거된다.

원소 타입을 컴파일타임에만 검사하며 런타임에는 알 수조차 없다는 뜻이다.

소거는 제네릭이 지원되기 전의 레거시 코드와 제네릭 타입을 함께 사용할 수 있게 해주는 메커니즘으로, 자바 5가 제네릭으로 순조롭게 전환될 수 있도록 해줬다.

이러한 차이로 인해 배열과 제네릭은 잘 어우러지지 못한다.

예컨대 배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.

제네릭 배열을 만들지 못하게 막은 이유는 타입 안전하지 않기 때문이다.

이를 허용한다면 컴파일러가 자동 생성한 형변환 코드에서 런타임에 ClassCastException이 발생할 수 있다.

E, List<E>, List<String> 같은 타입을 실체화 불가 타입이라 한다.

쉽게 말해, 실체화되지 않아서 런타임에는 컴파일타임보다 타입 정보를 적게 가지는 타입이다.

소거 메커니즘 때문에 매개변수화 타입 가운데 실체화될 수 있는 타입은 List<?>Map<?,?> 같은 비한정적 와일드카드 타입뿐이다.

배열을 비한정적 와일드카드 타입으로 만들 수는 있지만, 유용하게 쓰일 일은 거의 없다.

배열을 제네릭으로 만들 수 없어 귀찮을 때도 있다.

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

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

가변인수 메서드를 호출할 때마다 가변인수 매개변수를 담을 배열이 하나 만들어지는데, 이때 그 배열의 원소가 실체화 불가 타입이라면 경고가 발생하는 것이다.

이 문제는 @SafeVarags 애너테이션으로 대처할 수 있다.

배열로 형변환할 때 제네릭 배열 생성 오류나 비검사 형변환 경고가 뜨는 경우 대부분은 배열인 E[] 대신 컬렉션인 List<E>를 사용하면 해결된다.

코드가 조금 복잡해지고 성능이 살짝 나빠질 수도 있지만, 그 대신 타입 안정성과 상호운용성은 좋아진다.

0개의 댓글