아이템 25. 배열 대신 리스트를 써라.
배열 대신 리스트를 써라.
배열과 리스트의 차이점
- 배열 : 공변 자료형(covariant)
Sub extends Super
이면Super[] s = new Sub[];
가능- 실체화(reification)되는 자료형으로 원소의 자료형이 실행시간(runtime)에 결졍됨
- 리스트 : 불변 자료형(invariant)
Sub extends Super
이면List<Super> s = new ArrayList<Sub>();
불 가능
// 실행 중에 예외 발생
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // ArrayStoreException 예외 발생
// 하지만, 아래의 코드는 컴파일 되지 않음
List<Object> objectList = new ArrayList<Long>(); // 자료형 불일치로 컴파일 되지 않음
objectList.add("I don't fit in");
좀더 자세한 내용은 (Effective Java 2/E 162p~163p 참조)
interface Function<T>{
T apply(T arg1, Targ2);
}
// reduce의 제네릭 버전, 컴파일 되지 않는다.
static <E> E reduce(List<E> list, Function<E>, E initVal){
E[] snapshot = list.toArray(); // 내부적으로 리스트에 락을 건다.
E result = initVal;
for( E e : snapshot )
result = f.apply(result, e);
return result;
}
위 코드를 실행하면, 아래와 같은 에러 메시지가 발생한다.
Reduce.java:12: incompatible types
found : Object[], required: E[]
E[] snapshot = list.toArray();
^
cast문제로 생각되어 아래와 같이 고치면,
E[] snapshot = (E[])list.toArray();
위와 같이 코드를 수정하면, 오류는 사라지지만, 대신 아래와 같은 경고 메시지가 발생한다.
Reduce.java:12: warning: [unchecked] unchecked cast
found : Object[], required: E[]
E[] snapshot = (E[])list.toArray();
^
컴파일러가 전하려는 메시지는 실행 도중에 형 변환이 안전하게 이루어질지 검사 할수 없다는 뜻이다. 실행 시에 타입제거(Type Erasure)로 인해 E가 무슨 자료형이 될지 알수 없다.
안전한 형 변환 검증을 위해서 리스트를 사용하자, 아래의 코드는 실행 도중에 ClassCastException이 발생하지 않는다.
// 리스트를 사용하는 제네릭 버전
static <E> E reduce(List<E> list, Function<E>, E initVal){
List<E> snapshot;
synchronized(list){
snapshot = new ArrayList<E>(list);
}
E result = initVal;
for( E e : snapshot )
result = f.apply(result, e);
return result;
}
결론
제네릭과 배열이 따르는 자료형 규칙이 서로 많이 다르다. 만약 배열과 제네릭을 뒤섞어 쓰다가 컴파일 오류나 경고메시지를 만나게 되면 배열을 리스트로 바꾸자.
구분 | Type | 실체화(구체화) | 형 안정성 |
---|---|---|---|
배열 | 공변 자료형(covariant) | 가능 | Run time에 보장 |
제네릭 | 불변 자료형(invariant) | 불가능 | Compile time에 보장 |
E
, List<E>
, List<String>
등과 같은 자료형은 구체화 불가능(non-reifiable)자료형으로 컴파일시에는 타입 정보를 알수 있으나
런타임 시에 타입제거(Type erasure)로 인해 타입의 모든 정보를 런타임시에 알수가 없다. 반대로, primitive, non-generic, raw 타입, 비한정 와일드 카드형 타입(<?>
)
등은 구체화 가능 자료형(reifiable)이다.
참고 : https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html