Item 28 : 배열보다는 리스트를 사용하라
배열과 제네릭타입의 차이는 다음과 같다.
- Sub가 Super의 하위 타입이라면 Sub[]는 배열 Super[]의 하위타입이다(공변성)
- 서로다른 타입 Type1 Type2가 있을때 List<Type1> List<Type2>는 연관성이 없다.
이런 경우에 배열에 문제가 생기는데
Object[] objectArray = new Long[1];
objectArray[0] = "타입이 달라 넣을 수 없다." // ArrayStoreException 발생
이 배열 코드는 문법상 오류는 없지만 런타임시에 오류가 난다.
List<Object> ol = new ArrayList<Long>(); // 호환되지 않는 타입이다.
ol.add("타입이 달라 넣을 수 없다.")
이 코드는 컴파일시에 오류를 잡아줘서 문법상 오류를 바로 잡아준다.
개발자는 컴파일 시 오류를 잡아주는 것을 더 선호할 것이다.
두 번째 주요한 차이로, 배열은 실체화 된다는 점이다.
- 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.
- 반면 제네릭은 타입 정보가 런타임에는 소거된다.(컴파일 타임에만 원소 검사)
이러한 차이 떄문에 제네릭 배열을 만들지 못하게 하는데 예제를 확인해보자.
List<String>[] stringLists = new List<String>[1]; //(1)
List<Integer> intList = List.of(42); //(2)
Object[] objects = stringLists; //(3)
objects[0] = intList; //(4)
strings = stingList[0].get(0); //(5)
1번의 경우를 허용해 준다고 해보자
2번에서는 크기가 1인 intList를 생성할 것이다.
3번에서는 1번의 배열을 Object 배열에 할당한다 -- 여기까지는 문제가 생기지 않는다( 공변 )
4번에서는 2번에서 생성한 List<Integer> 의 인스턴스를 Object 배열의 첫 원소로저장한다.
제네릭은 소거방식이라 이 방법 또한 성공한다.
--> 런타임시에 List<Integer 가 단순 List로 변한다.
문제는 stringList는 List<String> 만 저장하겠다고 했는데 List<Integer>가 저장되어있다.
그래서 5번을 실행하면 ClassCastException을 던진다.
이런 일을 미연에 방지하기 위에서 1번 단계에서 오류를 내야하는 것이다.
소거 메커니즘 때문에 E, List<E>, List<String> 같은 타임은 실체화 불가 타입이라 한다.
위와 같은 사례때문에 배열과 제네릭을 함께 사용하기가 여간 까다로운것이 아니다.
다음 예시를 통해 배열과 제네릭이 얼마나 까다롭게 작용하는지 확인해보자.
다음 클래스는 컬렉션 안의 원소 중 하나를 무작위로 선택에 반환하는 choose 메소드를 제공한다.
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 List<T> choiceArray;
public Chooser(Collection<T> choices){
choiceArray = new ArrayList<>(choices)
}
public Object choose(){
Random rnd = ThreadLocalRandom.current();
return choiceArray.get(rnd.nextInt(choiceList.size()));
}
}
이렇게 만들면 런타임에 ClassCastException을 만나지 않게 된다.
'책 > 이펙티브자바' 카테고리의 다른 글
Item 30 : 이왕이면 제네릭 메소드로 만들라 (0) | 2021.07.29 |
---|---|
Item 29 : 이왕이면 제네릭 타입으로 만들라 (0) | 2021.07.29 |
Item 27 : 비검사 경고를 제거하라 (0) | 2021.07.26 |
Item 26 : 로 타입을 사용하지 말라 (0) | 2021.07.25 |
Item 25 : 탑클래스는 한 파일에 하나만 담으라 (0) | 2021.07.25 |