Search

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

생성일
2023/07/25 05:51
챕터
5장 - 제네릭

한정적 와일드카드를 사용하자

제네릭은 불공변성을 가지기 때문에, List<String>은 List<Object>를 받을 수 없다.
public class Stack<E> { public Stack(); public void push(E e); public E pop(); public boolean isEmpty(); public void pushAll(Iterable<E> src) { for (E e : src) push(e); } }
Java
복사
위 Stack 클래스의 pushAll 메서드는 오류나 경고없이 컴파일되지만, Stack<Number>로 선언 후 pushAll(intVal)(intVal은 Integer 타입)을 호출하면 오류가 발생한다.
public void pushAll(Iterable<? extends E> src) { for (E e : src) push(e); }
Java
복사
해당 메서드를 위와 같이 extends로 하한 경계 와일드카드(lower bound wildcards)를 적용하여 수정하면 문제없이 잘 동작한다.
public void popAll(Collection<E> dst) { while (!isEmpty()) dst.add(pop()); }
Java
복사
위와 같이 값을 꺼낼 때에도 한정적 와일드 카드를 적용하면 오류 없이 동작 시킬 수 있다.
public void popAll(Collection<? super E> dst) { while (!isEmpty()) dst.add(pop()); }
Java
복사
여기에서는 super로 상한 경계 와일드카드(upper bound wildcards)를 사용했는데, 이와 같이 유연성을 높이기 위해 원소의 생산자나 소비자용 매개변수에 와일드카드를 적용하는 것이 좋다.
다만 주의해야할 점은 반환 타입에 한정적 와일드카드 타입을 적용하면, 클라이언트에서 와일드카드를 통해서 받아야하므로 반환 타입에는 와일드카드를 사용하면 안된다.

PECS

PECS는 Producer-extends, Consumer-super의 약자로, 매개변수화 타입 T가 생산자라면 <? extends T>를 사용하고 소비자라면 <? super T>를 사용하라는 공식이다.
위의 Stack의 예시에서 pushAll의 경우 Stack이 사용할 E 인스턴스를 생산하므로 <? extends E>를 적용하고, popAll의 경우에는 Stack에서 데이터를 꺼내 소비하므로 <? super E>를 적용한다.