배열과 제네릭 자료형(List)의 차이
배열은 convariant 제네릭 자료형은 invariant 자료형이다.
차이 1.
covariant
- Sub[] 이 Super 의 하위자료형이라면 Sub[] 은 Super[]의 서브 타입이다.
invariant
Type1 Type2 List<Type1> List<Type2> 의 서브타입도 슈퍼타입도 아니다.
그렇기 때문에 List<Type1>은 List<Type2>의 서브타입 또는 슈퍼타입도 될 수 없다.
이런 이유로 제네릭 쪽이 배열보다 취약한 것 같지만 다음과 같은 문제를 보면
오히려 문제가 발생하는 부분은 배열이다.
1 2 3 4 5 6 7 8 | // 실행 중에 문제를 일으킴 (배열) Object[] objectArray = new Long[1]; objectArray[0] = "babo ya"; => 컴파일시 문제는 없지만 런타임 시 ArrayStoreException 예외가 발생한다. // 컴파일시 문제가 발생하는 코드 (제네릭) List<Object> ol = new ArrayList<Long>(); ol.add("babo ya"); | cs |
배열과 제네릭 모두 Long 객체 컨테이너안에 String 객체를 넣을 수는 없다.
그러나 배열은 위와 같이 실수를 유발할 수 있기 때문에 제네릭 타입이 더 좋다.
차이 2.
배열은 실체화(reification) 되는 자료형이다.
-> 배열의 각 원소의 자료형은 런타임시에 결정된다.
-> 위의 예를 통해 보면 알 수 있지만, String객체를 Long 배열에 넣는것이 컴파일시에는 문제가 발생하지 않는다는것이다.
리스트는 컴파일 시점에만 적용되고, 자료형 정보는 실행될 때는 삭제된다.
이런 차이들로 인해 배열과 제네릭(List)는 섞어서 사용하기 어렵다.
그렇기 때문에 new List<E>[], new List<String>[], new E[]는 전부 컴파일 되지 않는 오류이다.
이로인해 제네릭 배열을 만들 수 없다는 것이 불편함을 야기할 수 있다.
그렇기에 List<E>를 써서 배열을 대신할 수 있다. 대신 불편하지만 형안전성을 보장받을 수 있다.
그리고
배열은 컴파일시 리스트는 런타임시 체크를 하기 때문에
다음과 같이 list의 toArray에 캐스팅을 하게되면 런타임시 E가 무슨 자료형이 될지 알 수 없기 때문에 아래와 같은 경고 메시지가 출력된다.
이는 제네릭(List)는 불변 자료형이며, 실행시간에 형인자의 정보는 삭제가 되기 때문에 배열로 변경될 시 형 안정성을 보장하지 못하기 때문에 오류가 발생하는 것이다.
그래서 위와 같은 경우에는 배열이 아닌 ArrayList를 사용해야 한다.
1 2 3 | <E> E reduce(List<E> list) { List<E> snapshot = new ArrayList<>(list); } | cs |
결론으로
배열과 제네릭은 쉽게 혼용될 수 없다.
서로 혼용해서 사용하다가 컴파일 오류나 경고 메시지를 만나게 되면, 배열을 리스트로 바꿔서 사용해야 한다는 것을 기억해야 한다.
출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙25 인용.
'JAVA > Effective Java' 카테고리의 다른 글
제네릭 - 규칙 27 가능하면 제네릭 메서드로 만들 것 (0) | 2018.05.29 |
---|---|
제네릭 - 규칙 26 가능하면 제네릭 자료형으로 만들 것 (0) | 2018.05.29 |
제네릭 - 규칙 24 무점검 경고를 제거하라 (0) | 2018.05.29 |
제네릭 - 규칙 23 새 코드에는 무인자 제네릭 자료형을 사용하지 마라 (0) | 2018.05.29 |
클래스와 인터페이스 - 규칙22 멤버 클래스는 가능하면 static으로 선언하라. (0) | 2018.05.29 |