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

eversong·2020년 9월 2일
0

EffectiveJava

목록 보기
35/35
post-thumbnail

Wildcard

매게 변수화 타입은 불공변(invariant)이다. 즉 서로 다른 타입 Type1과 Type2가 있을때 List<Type1>List<Type2>의 하위 타입도 상위 타입도 아니다.

List<String>List<Object>와 아무 관계도 아니라는 말이다.

이처럼 매개변수화 타입은 고정되어 있지만 때로는 좀 더 유연하게 사용하고 싶은 경우가 있을 수 있다.

유연하지 않은 매개변수화 타입

예시

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

여기에 일련의 원소를 스택에 넣어보자

와일드 카드 타입을 사용하지 않은 경우 - pushAll


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

만약에 이 Stack을 Number로 선언한 뒤 pushAll(intVal)을 호출하면 어떻게 될까? 여기서 intVal은 Integer 타입이다

Stack<Number> numberStack = new Stack();
Iterable<Integer> integers = ...;
numberStack.pushAll(integers); // 오류 메세지!

이렇듯이 매개 변수화 타입은 유연하게 사용하기 어렵다

유연하게 사용하기

한정적 와일드 카드 타입

자바는 이러한 상황에 대처할 수 있는 한정적 와일드 카드 타입을 지원한다

pushAll의 입력 매개변수 타입은 'E의 Iterable'이 아니라 'E의 하위 타입의 Iterable'이여야 한다.

매개변수에 적용하기

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

이렇게 수정해주면 해결 가능하다!

이번엔 pushAll과 짝을 이루는 popAll을 살펴볼 차례다

와일드 카드 타입을 사용하지 않은 경우 - popAll

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

이번에도 문제는 발생한다. Stack<Number>의 원소를 Object용 컬렉션으로 옮기려 한다고 해보자.

Stack<Number> numberStack = new Stack();
Collection<Object> objects = ...;
numberStack.popAll(objects); // 오류 발생!!

이 코드는 하위 타입이 아니라는 오류가 발생한다.

이번에도 와일드 카드 타입으로 해결 가능하다

와일드 카드 타입에 사용한 경우

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

이제 모두 말끔히 컴파일 된다.

정리

유연성을 극대화 하기 위해서는 생산자나 소비자용 매개변수에 와일드 카드 타입을 사용하자.

PECS: producer-extends, consumer-super

일련의 공식으로 외워두면 어떤 와일드 카드 타입을 써야할지 알 수 있을것이다.

profile
책, 글, 개발

0개의 댓글