아이템 28에서 이야기했듯 매개변수화 타입은 불공변이다.
서로 다른 타입 A와 B가 있을 때 List<A>는 List<B>의 하위타입도 상위타입도 아니다.
List<String>은 List<Object>의 하위 타입이 아니라는 뜻이다.
때로는 유연성을 추구해야 할 때가 있다.
public class Stack<E>{
....
}
위와 같은 제네릭 클래스가 있다.
이 Stack에 데이터를 넣기 위해서 Iterable를 제공해야 할때, E의 타입의 하위 타입도 넣을 수 있어야 한다.
public void push(Iterable<? extends E> src){
....
}
이 때 이와같이 한정적 와일드카드를 사용할 수 있다.
만약 와일드카드를 사용하지 않으면 매개변수화 타입이 불공변이기 때문에 컴파일은 되지만 동작 시 에러가 발생한다.
이렇게 한정적 와일드 카드는 제네릭 타입 매개변수의 유연성을 높이기 위해 사용할 수 있다.
상위 타입이 매개변수로 필요하다면 extends말고, super 키워드를 사용하자.
그러나 어느 곳에 하위타입을 사용해야 하고 상위 타입을 사용해야 하는지 기억이 안날 수 있다.
이 때 PECS(펙스) : producer-extends, consumer-super 공식을 외워두자.
매개변수화 타입 T가 생산자라면 < ? extends T >를 사용하고, 소비자라면 < ? super T > 를 사용하자.
단순히 생각해 특정 타입의 생산자에선 하위타입을 생산하고, 생산자가 보낸 결과물을 받는 소비자는 특정 타입의 상위 타입을 받는다면
어떤 타입을 받던 문제 될 일이 없어진다.
이 PECS 공식은 와일드카드 타입을 사용하는 기본 원칙이다.
와일드카드와 타입 매개변수는 공통되는 부분이 있어서, 메서드를 정의할 때 둘중 어느 것을 사용해도 괜찮을 때가 많다.
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);
어떤 선언이 더 나을까? 더 나은 이유는 무엇일까?
public API라면 간단한 두 번째가 낫다.
어떤 리스트든 이 메서드에 넘기면 명시한 인덱스의 원소들을 교환해 줄 것이다. 신경 써야 할 타입 매개변수도 없다.
메서드 선언에 타입 매개변수가 한 번만 나오면 와일드 카드로 대체하라.
조금 복잡해지더라도 와일드카드 타입을 적용하면 API가 훨씬 유연해진다.
여러가지 쓰임새가 있는 코드를 작성한다면 반드시 와일드카드 타입을 적절히 사용해줘야 한다.
처음 코드를 구상할 때부터, 와일드카드와 제네릭을 사용한 구조를 구상해보자.
질문 1: 한정적 와일드카드를 사용하는 이유는?
한정적 와일드카드를 사용하면 다양한 타입을 매개변수로 받을 수 있고, 특정 타입의 상속 타입을 받을 수 있어서 유연성이 증가한다.
또한, 타입에 대한 제한을 설정함으로써 제네릭에 대한 타입 안정성을 증가시킬 수 있다.
질문 2: 한정적 와일드카드를 사용할 때 주의해야 할 점은?
한정적 와일드 카드를 사용할 때는 상호 운용성을 생각해야 한다.
유연성이 높아지는 만큼 사이드 이펙트가 발생할 수 있으며, 코드에 대한 타입의 호환성을 잘 확인하여 사용해야 한다.
또한 PECS 원칙을 기억해서 유연성과 안정성을 확인해야 한다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
33. 타입 안정 이종 컨테이너를 고려하라 (0) | 2023.07.16 |
---|---|
32. 제네릭과 가변인수를 함께 쓸 때는 신중하라 (0) | 2023.07.16 |
29,30. 이왕이면 제네릭 타입으로 만들라 (0) | 2023.07.16 |
28. 배열보다는 리스트를 사용하라 (0) | 2023.07.16 |
27. 비검사 경고를 제거하라 (0) | 2023.07.16 |