제네릭은 불공변이다. 하지만 때로는 불공변 방식보다 유연하게 사용해야 할 수 있다. 이때 사용하는 것이 한정적 와일드카드이다.
■ Stack 예시
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);
}
위의 메서드에서 Stack<Number>로 선언한 후 pushAll(intValue)를 호출하면 Integer는 Number의 하위 타입이지만 불공변이기 때문에 오류가난다.
이를 해결하기 위해서 사용하는 것이 한정적 와일드카드 타입이다. E의 하위 타입은 허용이 된다.
public void pushAll(Iterable<? extends E> src){
for(E e : src)
push(e);
}
다음은 한정적 와일드 카드를 사용하여 Stack<Number>의 원소를 Object용 컬렉션으로 옮기는 코드이다. 한정적 와일드 카드를 사용하지 않고 Object용 컬렉션을 매개변수로 넘기면 에러가난다.
public void popAll(Collection<? super E> dst) {
while(!isEmpty())
dst.add(pop());
}
유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하라. 다음 공식을 외워두면 어떤 와일드카드 타입을 써야하는지 도움이 된다. 클래스 사용자가 와일드카드 타입을 신경 써야 한다면 그 API에 문제가 있을 가능성이 있다.
펙스(PECS) : producer-extends, consumer-super
■ 재귀적 한정 타입 메서드 개선
class RecursiveTypeBound {
public static <E extends Comparable<E>> E max(Collection<E> collection) {
if (collection.isEmpty()) {
// Exception Handling
}
E result = null;
for (E e : collection) {
if (result == null || e.compareTo(result) > 0) {
result = Objects.requireNonNull(e);
}
}
return result;
}
}
위의 max 메서드에 PECS 공식을 적용하여 유연성을 높일 수 있다. Comparable을 직접 구현하지 않고 직접 구현한 다른 클래스를 확장한 타입을 지원할 때 필요합니다.
// 변경 전
public static <E extends Comparable<E>> E max(Collection<E> collection)
// 변경 후(PECS 공식 2번 적용)
public static <E extends Comparable<? super E>> E max(Collection<? extends E> collection)
'Effective Java' 카테고리의 다른 글
[Effective Java] 아이템33 타입 안전 이종 컨테이너를 고려하라 (1) | 2021.05.31 |
---|---|
[Effective Java] 아이템32 제네릭과 가변인수를 함께 쓸 때는 신중하라 (0) | 2021.05.30 |
[Effective Java] 아이템30 이왕이면 제네릭 메서드로 만들라 (0) | 2021.05.29 |
[Effective Java] 아이템29 이왕이면 제네릭 타입으로 만들라 (0) | 2021.05.23 |
[Effective Java] 아이템28 배열보다는 리스트를 사용하라 (0) | 2021.05.23 |