스터디/이펙티브 자바
ITEM31. 한정적 와일드카드를 사용해 API 유연성을 높이라
Lucy_Ko
2022. 10. 13. 00:55
- 매개변수화 타입은 불공변(invariant)이다.
- 서로 다른 타입 Type1과 Type2가 있을 때 List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니다.
- List<String>은 List<Object>가 하는 일을 제대로 수행하지 못하니 하위 타입이 될 수 없다.
(리스코프 치환 원칙에 어긋난다.) ITEM10 - 때로는 불공변 방식보다 유연한 방식이 필요하다.
Stack의 API
public class Stack<E> {
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
}
여기에 일련의 원소를 스택에 넣는 메서드를 추가
public class Stack<E> {
public void pushAll(Iterable<E> src) {
for (E e : src) {
push(e);
}
}
}
Integer 는 Number 의 하위 타입이니 pushAll(Integer타입값)이 잘 동작할 것 같다.
import java.util.Stack;
class Example {
public static void main(String[] args) {
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers;
numberStack.pushAll(integers);
}
}
- 실제 매개 변수화 타입이 불공변이기 때문에 오류 메시지를 내뱉는다.
- 자바는 이런 상황에 대처할 수 있는 한정적 와일드카드 타입이라는 특별한 매개변수화 타입을 지원한다.
와일드카드 타입 적용
class Example {
public void pushAll(Itrable<? extends E> src) {
for (E e : src) {
push(e);
}
}
}
- 유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용해야 한다.
- 입력 매개변수가 생산자와 소비자 역할을 동시에 한다면 와일드 카드 타입을 써도 좋을 게 없다.
- 타입을 정확히 지정해야 하는 상황으로, 이때는 와일드 카드 타입을 쓰지 않아야 한다.
펙스(PECS): producer-extends, consumer-super
- 와일드카드 타입을 써야하는지에 대한 상황을 분별할 수 있는 기준
- 매개변수화 타입 T가 생상자인경우 <? extends T>를 사용하고, 소비자인 경우 <? super T>를 사용해야 한다.
- Stack 예
- pushAll의 src 매개변수는 Stack이 사용할 E 인스턴스를 생산하므로 src의 적절한 타입은 Iterable<? extends E>이다.
- popAll의 dst 매개변수는 Stack으로부터 E 인스턴스를 소비(RETURN 반환)하므로 dst의 적절한 타입은 Collection<? super E> 이다.
- pushAll의 src 매개변수는 Stack이 사용할 E 인스턴스를 생산하므로 src의 적절한 타입은 Iterable<? extends E>이다.
- PECS 공식은 와일드카드 타입을 사용하는 기본원칙
ITEM30 의 union 코드 수정
class Example {
public static <E> Set<E> union(Set<? extends E> si, Set<? extends E> s2) {
Set<E> result = new HashSet<>(sl);
result.addAll(s2);
return result;
}
}
class Client {
public static void main(String[] args) {
Set<Integer> integers = Set.of(1, 3, 5);
Set<Double> doubles = Set.of(2.0, 4.0, 6.0);
Set<Number> numbers = union(integers, doubles);
}
}
정리
- 조금 복잡하더라도 와일드 카드 타입을 적용하면 API가 훨씬 유연해진다.
- 널리 쓰일 라이브러리를 작성한다면 반드시 와일드카드 타입을 적절히 사용해야 한다.
- PECS 공식을 기억하자
- 생산자(producer)는 extends를 소비자(consumer)는 super를 사용한다.
- Comparable과 Comparator는 모두 소비자라는 사실을 잊지 않아야 한다.