본문 바로가기

Dev Book Review/Effective Java

[Effective Java] item31. 한정적 와일드 카드를 사용해 API 유연성을 높여라

1. 매개변수화 타입의 불공변

매개변수화 타입은 불공변이다(invariant) : 서로 다른 타입 Type1,Type2가 있을 때 List<Type1>List<Type2> 의 하위타입도 아니고 상위타입도 아니다. (리스코프 치환 원칙에 어긋난다.)

이때 불공변 방식보다 유연한 방식 : 한정적 와일드카드 타입

 

2. 한정적 와일드 카드 타입을 이용한 확장

유연성 극대화를 위해 원소의 생산자나 소비자용 입력 매개변수에 왈일드카드 타입을 사용하라.

입력 매개변수가 생산자와 소비자 역할을 동시에 한다면 와일드 카드 타입을 써도 좋을 게 없다.

public void pushAll(Iterable<? extends E> src) { // 생산자
    for (E e : src)
    push (e);
}
public void popAll(Collection<? super E> dst) { // 소비자
  dst.addAll(list);
  list.clear();
}

PECS : producer-extends, consumer-super (= Get and Put Princlple)

매개변수화 타입 T가 생산자면 <? extends T>를 사용하고 소비자라면 <? super T>를 사용하라

 

3. 반환타입에서의 한정적 와일드 카드 타입

반환타입에는 한정적 와일드 카드 타입을 사용하면 안된다
유연성을 높여주지 않고 클라이언트 코드에서도 와일드 카드 타입을 사용하게 하기 때문이다.

public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)

클래스 사용자가 와일드 카드 타입을 신경써야 한다면 그 API에 문제가 있을 가능성이 크다

 

4. 매개 변수와 인수

  • 매개변수(parameter) : 메서드 선언에 정의한 변수 void add(int value)
  • 인수(argument) : 메서드 호출 시 넘기는 '실제 값' add(10)
  • 타입 매개변수(type parameter) : Class Set<T> {...}
  • 타입 인수(type argument) : Set<Integer> = ...

 

5. 예시

public static <E extends Comparable<? super E>> E max(List<? extends E> list)

이렇 게 구현했을 때만 아래 리스트를 max로 처리하는 것이 가능하다.

List<ScheduledFuture<?>> scheduledFulter = ... ;

 

6. 타입매개 변수가 하나이면 와일드카드로 대체하기

메서드 선언에 타입매개변수가 한번만 나오면 와일드 카드로 대체하라

비한정적 타입 매개변수 -> 비한정적 와일드 카드
한정적 타입 매개변수 -> 한정적 와일드 카드

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);

와일드 카드 타입을 사용하면, set, add와 같은 추가하는 메서드를 작성하지 못한다 따라서 실제 타입으로 변환해주기 위해 제네릭의 타입 매개변수를 사용해야하는데 이때

와일드 카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드를 따로 작성한다.
실제 타입을 알아내려면 이 도우미 메서드는 제네릭 메서드여야하기 때문이다.

public class Swap {
    public static void swap(List<?> list, int i, int j) {
        swapHelper(list, i, j);
    }

    // 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
    private static <E> void swapHelper(List<E> list, int i, int j) {
        list.set(i, list.set(j, list.get(i)));
    }
}

외부에서는 와일드 타입 기반의 멋진 선언을 유지할 수 있다.