배열과 제네릭 타입에는 중요한 차이가 두가지 있다.
a가 b의 하위타입이라면 배열 a[]는 배열 b[]의 하위 타입이 된다. ( 공변 )
그러나 제네릭은 불 공변이라 List<a>는 List<b>와 하위 타입도 아니고 상위 타입도 아니다.
이것만 보면 제네릭에 문제가 있는 것 같지만, 사실 문제가 있는 것은 배열 쪽이다.
다음은 문법상 허용되는 코드다.
Object[] objectArray = new Long[1];
objectArray[0] = "타입";
// 런타임에 실패, ArrayStoreException 발생
하지만 다음 코드는 문법에 맞지 않는다.
List<Obeject> ol = new ArrayList<Long>();
ol.add("타입");
// 컴파일 불가, 호환되지 않는 타입
어느쪽이든 Long 저장소에 String을 넣을 수 없지만, 어느 시점에 문제를 알아채느냐에 차이가 있다.
물론 컴파일 시에 알아채는 쪽이 선호될 것이다.
두 번째 주요 차이로, 배열은 실체화 된다.
배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.
하지만 제네릭은 타입 정보가 런타임에는 소거된다.
원소 타입을 컴파일 타임에만 검사하고, 런타임에는 알 수 없다는 뜻이다.
소거는 제네릭 지원 전, 레거시 코드와 제네릭이 함께 사용될 수 있도록 해주는 메커니즘이다. ( 로 타입 )
이러한 차이로 배열과 제네릭은 잘 어우러지지 못한다.
즉, 배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.
제네릭 배열을 만들지 못하게 막은 이유는 타입이 안전하지 않기 때문이다.
이를 허용한다면 컴파일러가 자동 생성한 형변환 코드에서 런타임에 캐스팅 에러가 발생할 수 있다.
런타임에 캐스팅 에러 발생을 막아주겠다는 제네릭 타입 시스템의 취지에 어긋나는 것이다.
책의 예제에서는 배열을 제네릭으로 만들기 위한 여러가지 과정을 수행한다.
허나 나는 클론코딩을 통해 제네릭을 많이 사용하면서, 배열로 제네릭을 사용해본 적이 기억이 나지 않는다..
책에 나오듯 제네릭은 항상 List를 이용해서 사용했었다.
배열을 제네릭으로 사용하기 위해 여러가지 타입을 안전하게 캐스팅 할 수 있도록 하는데,
특별한 경우를 제외하고 제네릭을 사용할 때는 List를 사용하도록 하자.
그리고 제네릭 사용 시 강조되는 것이 타입 안정성이다.
코드의 유연성을 높이기 위해 제네릭과 비한정적 와일드 카드를 섞어서 사용한 경험이 여러번 있다.
이 때 타입 안정성을 위해 몇가지 장치를 마련했었다.
예를들면 커스텀 어노테이션을 이용해 특정 클래스들을 리플렉션하고, 리플렉션 한 클래스들을 저장 후 ,
호출 시에는 클래스에 선언해놓은 제네릭 매개변수(객체)를 기준으로 특정 클래스를 호출하도록 했었다.
매개변수인 객체를 기준으로 클래스를 찾기 때문에, 그 클래스에서 사용되는 파라미터 객체 외엔 다른 타입이 침범할 수 없다.
이런식으로 타입 안정성을 확보하기 위해 몇가지 디자인 패턴을 적용시켰었다. 이에 대해선 나중에 포스팅 할 예정이다.
배열과 제네릭에는 매우 다른 타입 규칙이 적용된다.
배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입 정보가 소거된다.
그래서 둘을 섞어 쓰기란 쉽지 않다. 둘을 섞어 쓰다가 경고를 만나면 배열을 리스트로 대체하는 방법을 적용해보자.
질문 1. 제네릭 사용 시 배열과 리스트를 비교하여 리스트를 사용해야 하는 이유는?
리스트를 사용 시 컴파일 타임에 타입 캐스팅이 안전한지 확인할 수 있다.
반면 배열은 런타임 환경에서 타입 캐스팅 에러가 발생하기 때문에 코드의 에러를 부정확하게, 뒤늦게 확인할 수 있다.
또한 배열과 비교하여 리스트는 크기를 동적으로 조절해 배열보다 높은 유연성을 가질 수 있다.
질문 2: 배열 대신 리스트를 사용할 때 주의할 점은?
리스트는 배열에 비해 성능 오버헤드가 일어날 수 있고, 랜덤 액세스와 같은 특정 연산에서 리스트보다 성능이 떨어질 수 있다.
또한 기존 코드나 라이브러리가 배열을 사용한다면 리스트로 전환하기에 제약사항이 있을 수 있다.
상황에 따라 적절히 선택하여 사용해야 한다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
31. 한정적 와일드카드를 사용해 API 유연성을 높이라 (0) | 2023.07.16 |
---|---|
29,30. 이왕이면 제네릭 타입으로 만들라 (0) | 2023.07.16 |
27. 비검사 경고를 제거하라 (0) | 2023.07.16 |
26. 로 타입을 사용하지 말라 ( 제네릭, 타입 안정성 ) (0) | 2023.07.10 |
25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2023.07.10 |