아이템 28에서 이야기했듯 매개변수화 타입은 불공변
(invariant)이다.
즉 서로 다른 타입 Type1과 Type2가 있을 때 List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니다.
하지만 때론 불공변 방식보다 유연한 무언가가 필요하다.
유연성을 높이기 위해, 원소의 생산자(producer)나 소비자(consumer)용 입력 매개변수에 와일드카드 타입을 사용하자.
팩스(PECS) 란, producer-extends, consumer-super 를 의미한다.
생산자
라면 <? extends T>
를 사용한다.소비자
라면 <? super T>
를 사용한다.그 동안 살펴본 예제 중 생산자 매개변수에 와일드카드 타입을 적용해보자.
// Stack
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
public Chooser(Collection<? extends T> choices) {
choiceList = new ArrayList<>(choices);
}
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)
단, 반환 타입에는 한정적 와일드카드 타입을 사용하면 안 된다.
유연성을 높여주기는커녕 클라이언트 코드에서도 와일드카드 타입을 써야 하기 때문이다.
다음은 소비자 매개변수에 와일드카드 타입을 적용한 예시이다.
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
아이템 30에서의 max 메서드에도 적용해보자.
public static <E extends Comparable<? super E>> E max(List<? extends E> list)
Comparable
은 언제나 소비자다. Comparator
도 마찬가지이다.Comparable<? super E>
Comparator<? super E>
타입 매개변수와 와일드카드를 모두 사용할 수 있는 경우에는 어떤 것을 사용해야 할까?
// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
private static <E> void swapHelper(List<E> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
public static void swap(List<?> list, int i, int j) {
swapHelper(list, i, j);
}
📌 핵심 정리
- 조금 복잡하더라도 와일드카드 타입을 적용하면 API가 훨씬 유연해진다.
- 그러니 널리 쓰일 라이브러리를 작성한다면 반드시 와일드카드 타입을 적절히 사용해줘야 한다.
- PECS 공식을 기억하자.
즉, 생산자(producer
)는extends
를, 소비자(consumer
)는super
를 사용한다.
Comparable과 Comparator는 모두 소비자라는 사실도 잊지 말자.