Language/Java
[Effective Java] 아이템31: 한정적 와일드카드를 사용해 API 유연성을 높이라
wisdom11
2022. 8. 6. 09:34
아이템 28에서 이야기했듯 매개변수화 타입은 불공변(invariant)이다.
즉 서로 다른 타입 Type1과 Type2가 있을 때 List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니다.
하지만 때론 불공변 방식보다 유연한 무언가가 필요하다.
유연성을 높이기 위해, 원소의 생산자(producer)나 소비자(consumer)용 입력 매개변수에 와일드카드 타입을 사용하자.
✔️ PECS
팩스(PECS) 란, producer-extends, consumer-super 를 의미한다.
- 매개변수화 타입 T가
생산자
라면<? extends T>
를 사용한다. - 매개변수화 타입 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());
}
✔️ Comparable, Comparator
아이템 30에서의 max 메서드에도 적용해보자.
public static <E extends Comparable<? super E>> E max(List<? extends E> list)
Comparable
은 언제나 소비자다.Comparator
도 마찬가지이다.- Comparable<E> →
Comparable<? super E>
- Comparator<E> →
Comparator<? super E>
✔️ 타입 매개변수 vs 와일드카드
타입 매개변수와 와일드카드를 모두 사용할 수 있는 경우에는 어떤 것을 사용해야 할까?
// 와일드카드 타입을 실제 타입으로 바꿔주는 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는 모두 소비자라는 사실도 잊지 말자.
728x90