-
[Effective Java] 아이템28: 배열보다는 리스트를 사용하라Language/Java 2022. 7. 30. 02:29
✔️ 배열 vs 제네릭 타입
1. 배열은 공변(covariant)인 반면, 제네릭은 불공변(invariant)이다.
Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 되지만, List<Sub>은 List<Super>의 하위 타입도 아니고 상위 타입도 아니다.
2. 배열은 실체화(reify)된다.
배열은 런타임에도 원소의 타입을 확인한다. 즉, 런타임에 예외가 발생할 수 있다.
반면, 제네릭은 타입 정보가 런타임에는 소거된다. 원소 타입을 컴파일타임에만 검사하며 런타임에는 알 수조차 없다.💡 위와 같은 차이로 인해, 배열과 제네릭은 잘 어우러지지 못한다.
예를 들어, 배열은 제네릭 타입(
new List<E>[]
), 매개변수화 타입(new List<String>[]
), 타입 매개변수(new E[]
)로 사용할 수 없다.✔️ Chooser 클래스 예제
생성자에서 컬렉션을 받고, 컬렉션 안의 원소 중 하나를 무작위로 선택해 반환하는
choose
메소드를 제공하는Chooser
클래스를 구현해보자.📍 제네릭을 쓰지 않은 방법 → 제네릭 적용이 시급하다!
public class Chooser { private final Object[] choiceArray; public Chooser(Collection choices) { choiceArray = choices.toArray(); } public Object choose() { Random rnd = ThreadLocalRandom.current(); return choiceArray[rnd.nextInt(choiceArray.length)]; } }
이 클래스를 사용하려면 choose 메서드를 호출할 때마다 반환된 Object를 원하는 타입으로 형변환해야 한다.
이때 만약 다른 타입의 원소가 들어 있었다면 런타임에 형변환 오류가 발생할 것이다.따라서 이 클래스는 제네릭 타입으로 만들어야 한다.
📍 제네릭으로 만들기 위한 시도 → 경고
public class Chooser<T> { private final T[] choiceArray; public Chooser(Collection<T> choices) { // 1. 컴파일 오류 // choiceArray = choices.toArray(); // 2. 경고 choiceArray = (T[]) choices.toArray(); } public Object choose() { Random rnd = ThreadLocalRandom.current(); return choiceArray[rnd.nextInt(choiceArray.length)]; } }
1번
과 같이 작성할 경우, 배열Object[]
이 배열T[]
과 타입이 맞지 않아 컴파일 오류가 발생한다.이를 해결하기 위해
2번
과 같이 작성한 경우, 오류는 발생하지 않지만 런타임에 형변환이 안전한지 보장할 수 없다는 경고 메세지가 나온다.
이 코드의 경우, 동작하기는 하지만 컴파일러가 안전을 보장하지 못 한다.📍 리스트 기반 → 타입 안전성 확보
비검사 형변환 경고를 제거하려면 배열 대신 리스트를 쓰면 된다.
다음 코드는 오류나 경고 없이 컴파일된다.
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.nextInt(choiceList.size())); } }
핵심 정리
- 배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입 정보가 소거된다.
- 배열은 런타임에는 타입 안전하지만 컴파일타임에는 그렇지 않다.
제네릭은 그 반대다. - 이러한 이유로 배열과 제네릭을 섞어 쓰기란 쉽지 않다.
- 둘을 섞어 쓰다가 컴파일 오류나 경고를 만나면, 가장 먼저 배열을 리스트로 대체하는 방법을 적용해보자.
728x90'Language > Java' 카테고리의 다른 글
[Effective Java] 아이템30: 이왕이면 제네릭 메서드로 만들라 (0) 2022.08.06 [Effective Java] 아이템29: 이왕이면 제네릭 타입으로 만들라 (0) 2022.08.06 [Effective Java] 아이템27: 비검사 경고를 제거하라 (0) 2022.07.29 [Effective Java] 아이템26: 로 타입(raw type)은 사용하지 말라 (0) 2022.07.29 [Effective Java] 아이템25: 톱레벨 클래스는 한 파일에 하나만 담으라 (0) 2022.07.23