스터디/이펙티브 자바

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

📝 작성 : 2022.06.18  ⏱ 수정 : 
728x90

Item28에서 정리했던 것 처럼 매개변수화 타입은 불공변(invariant)입니다. 즉, List<String>은 List<Object>의 하위타입이 아닙니다.

일반적으로는 이렇게 되는 것이 맞는 것 같습니다. 하지만 List<Number>에 Integer 타입을 추가하고 싶을 때가 있습니다. 매개변수화 타입은 불공변이기 때문에 불가능하지만 해결책은 있습니다. 한정적 와일드카드를 이용하는 것 입니다.

한정적 와일드 카드(Bounded Wildcards)

  • Upper Bounded Wildcards: 타입제한을 풀어줄 때 사용, 제네릭 타입을 상위 제네릭 타입으로 묶을 때 사용 (List<? extends Number>)
  • Lower Bounded Wildcards: 타입을 제한할 때 사용, 유연성을 위해 지정된 타입의 상위타입만 허용할 때 사용 (List<? super Integer>)

둘 모두 자기 자신도 포함합니다. 즉 List<? super Integer>에는 Integer타입도 가능합니다.

아래의 결함이 있는 Stack 클래스를 보면서 실습해보겠습니다.

public class Stack<E> {
    public void push(E e) { ... }
    public E pop() { ... }
    public boolean isEmpty() { ... }

    public void pushAll(Iterable<E> src) {
        for (E e : src) {
            push(e);
        }
    }

    public void popAll(Collection<E> dst) {
        while (!isEmpty()) {
            dst.add(pop());
        }
    }
}

Stack<Number>에 Iterable<Integer>를 pushAll하거나 Collection<Object>를 popAll하고 싶을 때는 아래와 같이 바꿀 수 있습니다.

public void pushAll(Iterable<? extends E> src) { ... }
public void popAll(Collection<? super E> dst) { ... }

이처럼 유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개변수에 한정적 와일드카드 타입을 사용합니다. 한편, 입력 매개변수가 생산자와 소비자 역할을 동시에 한다면 타입을 정확하게 지정해야 하므로 와일드카드 타입을 사용하면 안됩니다.

팩스(PECS): producer-extends, consumer-super

위의 공식을 외우면 어떤 와일드카드 타입을 사용하는지 기억하는데 도움이 됩니다.

이때 주의할 점은 반환 타입에는 한정적 와일드카드 타입을 사용하면 안된다는 것 입니다.

반응형