Item28. 배열보다는 리스트를 사용하라
배열과 Generic type의 차이점
- 배열은 공변(covariant) 인 반면 Generic은 불공변(invariant) 이다.
Sub class가 Super class의 하위 class라고 가정하면 배열 Sub[]는 Super[]의 하위 타입이 된다.
반면에 서로 다른 타입인 Type1과 Type2가 있을떄 Generic인 List<Type1> List<Type2> 은 아무 계층관계도 가지지 않는다.
1 | // Array |
배열은 type으로 인해 runtime Exception이 발생할 수 있는 반면, generic인 compile 경고를 먼저 띄워준다.
- 배열은 실체화 된다.
배열은 runtime 에도 원소의 타입을 인지하고 확인한다, 예를 들면 Long 배열에 String을 넣으려고 하면 runtime에 이를 확인하고 예외를 발생시킨다.
반면 generic은 runtime에 타입을 소거한다 (type erasure). 원소의 타입을 compile time에만 검사하며 , runtime에는 모른다.
위와 같은 차이점 떄문에 Generic 배열은 Java에서 허용되지 않는다.
예시로 아래와 같이 List
1 | List<String>[] stringList = new List<String>[1]; |
실체화 불가 타입
E,List<E>,List<String>과 같은 type을 실체화 불가 타입이라고 한다. 실체화 불가 타입이란 runtime에는 compile time보다 타입 정보를 적게 가지는 타입이다. runtime시 타입 소거 떄문에 매개변수화 타입 중에 실체화 될 수 있는 타입은 List<?> 와 Map<?,?> 와 같은 비한정적 wildcard 타입 뿐이다.
Generic type을 배열로 형변환
1 | public class Chooser { |
이를 client에서 꺼내쓰려면 매번 형변환이 필요하다
1 | List<String> stringList = List.of("a1", "a2", "a3"); |
따라서 다음과 같이 generic collection을 생성자에서 받고, T 배열로 형변환하면 된다.
1 | public class Chooser<T> { |
1 | List<String> stringList = List.of("a1", "a2", "a3"); |
또는 배열과 Generic을 섞어 쓰는 상황에서는 아래와 같이 배열을 리스트로 아예 대체하는 해결방안도 제시하고 있다.
1 | public class Chooser<T> { |
정리
배열과 Generic은 다른 타입 규칙이 적용된다. 배열은 공변 , Generic은 불공변이다.
Generic은 compile time에 type 관련 error를 잡아주고 type이 지워진 bytecode를 만드는 반면, 배열은 runtime에도 type을 검사하고, type과 관련된 runtime exception이 터질 수도 있다.
Generic과 배열을 같이쓰는 상황에서는 배열을 List로 대체하는 것을 고려하자