#25 배열보다는 List를 사용하자
배열(Array)은 두 가지 관점에서 제네릭 타입과 다릅니다.
공변(convariant)이라는 단어를 처음 듣는데 뜻은 아래와 같습니다.
경험적 관계에서 첫 번째 변수의 크기가 두 번째 변수의 크기에 따라 변하는 것을 말한다.
http://terms.naver.com/entry.nhn?docId=1520193&cid=50298&categoryId=50298
예를 들면, 상속 관계인 Super 클래스와 Sub 클래스를 이용해 배열 Super[], Sub[]를 만들면 이 두 배열도 상속 관계라는 의미입니다.
Super 클래스 - Sub 클래스 (상속 관계)
Super[] - Sub[] (상속 관계)
이 와 반대로 제네릭은 불변(invariant)입니다.
예를 들면, 상속 관계인 Super 클래스와 Sub 클래스를 이용해 List<Super>, List<Sub>를 만들면 이 두 List는 상속 관계가 아닙니다.
Super 클래스 - Sub 클래스 (상속 관계)
List<Super> - List<Sub> (아무 관계 아님)
상속 관계를 표현 못하는 제네릭이 어찌 보면 더 안 좋아 보일 수 도 있습니다.
아래 코드는 Object-Long 상속 관계를 이용한 배열과 List(제네릭)입니다.
첫 번째 배열 코드는 컴파일에서는 문제가 없지만 런타임 에러가 발생합니다.
두 번째 List 코드는 컴파일 시에 에러가 발생합니다.
어떤 에러든 런타임 시보다 컴파일 시에서 미리 발견하는 게 더 좋습니다.
구체적(reified)라는 말은, 런타임 시에 자신의 요소 타입을 체크한다는 의미입니다.
위에서 본 이 코드를 보면, 배열은 컴파일이 아닌 런타임 시에 요소 타입을 체크해 예외를 발생시킵니다.
반대로 제네릭은 컴파일 시에 요소 타입을 체크하고 있습니다.
서로 요소 타입을 체크하는 시점이 다르기 때문에 타입 안전성의 문제로 배열과 제네릭은 보통 같이 쓰이지 않습니다.
다른 대안이 있는데 이는 다음에 알아보겠습니다.
배열과 제네릭은 서로 다른 타입 규칙을 가지고 있습니다.
배열 : 공변, 구체적
제네릭 : 불변, 비 구체적
배열은 런타임 시 타입 안전을 체크하지만 제네릭은 컴파일이 타입 안전을 체크하기 때문에, 배열보다는 리스트가 더 안전합니다.