Programming/JAVA

[Effective JAVA 3SE] item 28: 배열보다는 리스트를 사용하라

sky Jeong 2021. 7. 11. 12:44

서론

배열과 제네릭 타입에 두 가지의 중요한 차이가 존재함.


1 . 공변(convariant)여부

>> 배열은 공변임

 

- Sub(자식)이 Super(부모)의 하위 타입이라면 배열 Sub[ ]는 배열 Super[ ]의 하위 타입이라는 뜻

- 공변은 한자 풀이 그대로 함께 변화한다는 뜻임

 

>> 제네릭은 불공변임

 

- 서로 다른 타입 Type 1과 Type 2가 존재할 때, List<Type1>은 List<Type 2>의 하위 타입도 아니고 상위 타입도 아님

 

서로간의 상위 하위 타입 문제로 접근하자면 제네릭이 더욱 문제있어 보일 수 있지만, 문제가 있는 쪽은 배열임

공변이 왜 문제가되는지 예시 코드를 살펴보겠음.

 

위 코드는 문법상 허용되나 런타임 에러 발생
이는 컴파일 에러가 발생함

 

2. 실체화(Reify) 여부

>> 배열 실체화 가능

 

- 배열은 런타임에서 자신이 담기로 한 원소의 타입인지 확인함

- 그래서 위에서 보았듯 Long 배열에 String을 넣으면 ArrayStoreException이 발생함

 

>> 제네릭

 

- 제네릭은 타입 정보가 런타임에서 소거됨

[제네릭은 런타임에서 타입을 체크해 이를 할당하는 가변성을 가짐]

- 위 말은 컴파일에서 원소 타입을 알 수 없음

 

- 소거는 제네릭이 지원되기 전 레거시 코드와 제네릭 타입을 함께 사용할 수 이게 해주는 메커니즘으로 ㅈ바 5에서 제네릭으로 순조롭게 전환될 수 있게 해줌 (?????)

- 위 말은 new List<E>[ ], new List<String>[ ]과 같이 작성하면 컴파일 할 때, 제네릭 배열 생성 오류가 발생한다는 것

 


제네릭으로 배열을 만들지 못하는 이유

정리하자면 타입이 안전하지 않기 때문

- 해당 내용 추가 정리 필요

- 제네릭 배열은 타입이 안전하지 않기에 제네릭 배열을 만들지 못하게 막아져 있음

- 아에 컴파일 자체가 안되기도 함

 

- E, List<E>, List<String>은 실체화 불가 타입(non-refiable type)

-- 실체화되지 않아 런타임에서 컴파일타임보다 타입 정보를 적게 가지게 됨

-- 소거 매커니즘떄문에 매개변수 타입 가운데 실체화 가능 타입은 List<?>와 Map<?, ?> 과같은 비한정적 와일드카드 타입뿐임 (아이템 26)


 

  • 배열을 제네릭으로 만들 수 없어 귀찮을 수 있음
    제네릭 컬렉션에서 자신의 원소 타입을 담은 배열을 반환하는게 보통 불가능함 (이에 대한 해결법은 아이템 33)

  • 제네릭 타입과 가변인수 메서드를 함께 사용하면 경고 메시지를 받게 됨.
    이는 @SafeVarags 로 대처 가능 (아이템 32)

 


핵심 정리

  • 배열과 제네릭은 매우 다른 타입 규칙이 적용됨
  • 배열은 공변이고 실체화가 가능한 반면, 제네릭은 불공변에 타입 정보가 소멸됨
  • 이로 배열은 런타임에 안전하지 않지만, 컴파일타임에서는 그렇지 않음
  • 고로 두 가지를 섞어 사용하는 것은 쉽지 않음
  • 둘을 섞어 사용하다 컴파일 오류나 경고 발생시, 가장 먼저 배열을 리스트로 대체하는 방법 적용 할 것