아이템31. 한정적 와일드카드를 사용해 API 유연성을 높이라

wisdom·2022년 8월 19일
0

Effetctive Java

목록 보기
31/80
post-thumbnail

아이템 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는 모두 소비자라는 사실도 잊지 말자.
profile
백엔드 개발자

0개의 댓글