JVM/Java

[Java] 자바 제네릭 불공변 / 공변 / 반공변 처음 들어봅니다.

Hyo Kim 2022. 4. 22. 23:19
728x90
반응형

😀 서론

List<String>
List<E>
List<? extends E>
List<? super E>

당연하게 사용했던 자바의 제네릭은 여러 형태가 있다.

각각의 특징이 있고, 그 특징을 설명하는 용어를 알게 되었고, 정리해보려고 한다.


😗 본론

제목에 나와있는 3가지 특징을 한 번 정리하고 시작하겠습니다.

불공변 or 무공변 (Invariant) List<String>은 List<Object>의 하위타입이 아니다. 
공변 (Covariant) String 이 Object의 서브타입이면,
List<String>은 List<? extend Object> 의 서브타입이다.
반공변 (Contravariant) String 이 Object의 서브타입이면,
List<Object>은 List<? super String> 의 서브타입이다.

불공변

List<Integer>와 List<Number> 는 서로 다르다.

제네릭은 기본적으로 불공변입니다.

그렇기 때문에 아래와 같이 코드를 작성할 시 우리는 오류 메시지를 확인하게 됩니다.

public class Stack {

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

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

}

Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = ...;
numberStack.pushAll(integers); // Error 발생

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

공변

어떠한 T 타입의 공변성을 허용하려면 <? extend T>를 사용한다.
public void pushAll(Iterable<? extends E> src) {
	for (E e : src)
    	push(e);
}

Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = ...;
numberStack.pushAll(integers); // 깔끔한 컴파일

반공변

어떠한 T 타입의 반공변성을 허용하려면 <? super T>를 사용한다.
public void popAll(Collection<? super E> dst) {
    while (!isEmpty())
        dst.add(pop());
}

Stack<Number> numberStack = new Stack<>();
Collection<Object> objects = ...;
numberStack.popAll(objects); // 깔끔한 컴파일

공변 / 반공변은 왜 쓸까?

위 코드만 보더라도 제네릭을 더욱 유연하게 사용할 수 있게 해주는 걸 확인할 수 있습니다.

기본 제네릭 만으로는 하나의 타입밖에 표현할 수가 없고, 이는 리스코프 치환 원칙에도 어긋나게 됩니다.


언제 어떤걸 사용해야 할까?

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

위 공식을 외우게 되면, 어떤 와일드카드 타입을 써야 하는지 기억하는 데 도움이 될 것입니다.

 

즉, 생산자(producer)는 extends 를 사용하고,

소비자(consumer)는 super를 사용하면 됩니다.

 

반대로 사용할 경우 어차피 컴파일에서 에러를 잡아줍니다.


주의할 점

한정적 와일드카드는 반환 타입으로 설정하면 안 됩니다.

유연성을 높여주기커녕 클라이언트 코드에서도 와일드카드 타입을 사용해야 하기 때문입니다.


😆 결론

필요에 따라서 간혹 와일드카드를 사용하곤 했었는데 이러한 용어가 있다는 것을 알게 되어서

복습한다는 느낌으로 정리를 하면서 왜 나오게 되었는지도 알 수 있게 되어서 흥미로웠습니다.

 

 

이 내용은 이펙티브 자바3판 - 아이템 31 "한정적 와일드카드를 사용해 API 유연성을 높여라" 내용을 인용합니다.

728x90
반응형