아이템 28에서 우리는 매개변수화 타입은 불공변이라고 학습했다.

List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니라는 의미다.

근데 왜 List<String>은 List<Object>의 하위타입이 되지 못하는 것일까?

왜냐하면 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);
}

Stack 클래스에 pushAll 메소드를 추가하고 싶다.

그러나 pushAll에 들어가는 E가 Stack의 E와 다르면 제대로 작동하지 않을것이다.

그렇기 때문에

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

와일드 카드의 의미는 pushAll의 입력 매개변수 타입은 E의 Iterable이 아니라

E의 하위 타입의 Iterable이어야 한다는 의미이다.

 

이제 pushAll과 작을 이루는 popAll 메소드를 작성해야 한다

앞에서 배운것을 응용해서 만들어보자

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

여기서 와일드카드의 의미는 popAll의 입력 매개변수의 타입이 E의 Collection이 아니라

E의 상위 타입의 Collections이어야 한다는 의미이다.

 

즉 매개변수의 유연성을 활용하기 위한 와일드 카드의 활용법이다.

처음보면 헷갈릴 수 있으니 다음의 공식을 기억하는 것이 좋다

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

pushAll은 Stack이 사용할 인스턴스를 생성하는 것이니 producer이며

popAll은 Stack에서 뽑아오는 것이기 때문에 consumer이다.

 

 

---- 이번장에서의 포인트는 여기까지이다! 뒤에 부분은 조금더 심화된 부분이다.

타입 매개변수와 와일드 카드에는 공통되는 부분이 있어서 메소드를 정의할 때 둘중 어느것을 사용해도 괜찮을 때가 많다. 이런 상황에서 무엇을 선택해야 할까?

다음 예시를 보자

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);

public API라면 두번째가 낫다.

왜냐하면 어떤 리스트든 명시한 인덱스의 원소들을 교환해줄 것이기 때문이다.

 

기본 규칙은 이렇다 메소드 선언에 타입 매개변수가 한번만 나오면 와일드 카드로 대체한다.

 

이때 비한정적 타입 매개변수라면 비한정적 와일드카드로 바꾸고

한정적 타입 매개변수라면 한정적 와일드카드로 바꾸면된다.

 

그러나 두번째 경우는

public static void swap(List<?> list, int i, int j){
    list.set(i, list.set(j, list.get(i)));
}

이런식으로 직관적으로 구현한 코드로 만들면 작동을 안한다.

와일드카드에 null외에는 어떤 값도 넣을 수 없기 때문인데 이를 도와주는 private 메소드가 필요하다

public static void swap(List<?> list, int i, int j){
    swapHelper(list, i, j);
}

private static <E> void swapHelper(List<E> list, int i, int j){
    list.set(i, list.set(j, list.get(i)));
}

swapHelper 메소드는 리스트가 E임을 알고 있어서 오류가 나지 않는다.

+ Recent posts