본문 바로가기

Programming/Java

Java PECS (producer-extends, consumer-super) 에 관하여

Effective Java 2nd edition 을 요즘 보고 있는데, Java generics 에 관하여 좋은 글인 거 같아서 따로 복습도 할 겸 포스팅을 하게 되었다.


PECS 란 무엇인가 하면, Producer Extends, Consumer Super 라는 단어의 Initial 을 따서 붙인 것이다.

쉽게 설명을 하자면, 뭔가를 생산하는 일을 하면, 상속을 받고, 뭔가를 소비하게 되면 상속해 주는 것이다.


무슨 말인고 하니, Effective Java 2nd Edition 의 예를 들어보면,


Stack 의 일반화된 method 인 pushAll 을 보자.

     // 뭔가 부족한 pushAll method - parameterized types E 를 사용하여 구현

    public void pushAll(Iterable<E> src) {

        for (E e : src)

            push(e);

    }


왜냐하면, parameterized types 는  invariant 하기 때문이다. invariant 하다는 것이 무슨 뜻이냐면, 

두개의 구분된 Type1, Type2 가 있는데, List<Type1> 은 List<Type2> 의 supertype 도 아니고, subtype 도 아니라는 말이다. 

그러므로 아래와 같은 코드에서는 compile time 오류가 발생하게 된다. 

왜냐하면, Stack<Number> 와 Iterable<Integer> 는 서로 invariant 하기 때문이다.

      // 위의 method 는 아래와 같은 상황에서 compile error 를 보고하게 한다.

    Stack<Number> numberStack = new Stack<Number>();

    Iterable<Integer> integers = ...;

    numberStack.pushAll(integers);

 

그래서 위와 같은 compile 에러를 고치기 위해서는 아래와 같이 bounded wildcard type 을 사용하여,

아래와 같이 작성해야 올바른 코드가 되고, type safe 한 프로그램이 된다.

    // 개선된 pushAll method - parameter 의 wildcard type 은 E producer 로서 역할을 한다.

    public void pushAll(Iterable<? extends E> src) {

        for (E e : src)

            push(e);

    }


그래서 위의 method 의 입력 parameter, src 는 E 의 Iterable 이 되면 안되고

E 의 subtype 인 어떤 Iterable 이 되어야 한다는 것이다.

그리고 입력 paramter 인 src 는 Stack 클래스의 pushAll 의 자료를 공급하는 것(producer) 이기 때문에, 

extends 가 사용됨을 알 수 있다. PE (producer-extends)


Stack 클래스의 popAll 이라는 일반화된 method 를 살펴보자.

      // 뭔가 부족한 popAll method, parameterized type E 를 사용하여 구현되어 있다.

    public void popAll(Collection<E> dst) {

        while (!isEmpty())

            dst.add(pop());

    }


위의 method 도 아래와 같은 상황에서 compile time 에러가 발생한다.

왜냐하면, Collection<Object> 는 Collection<Number> 의 subtype 이 아니기 때문이다.

 Stack<Number> numberStack = new Stack<Number>();

 Collection<Object> objects = ... ;

 numberStack.popAll(objects);


그래서 아래와 같이 popAll method 의 입력 parameter 인 dst 는 E 의 supertype 인 어떤 Collection 이라고 명시해 주면 된다.

 // wildcard type 은 E consumer 로서 역할 을 한다.

 public void popAll(Collection<? super E> dst) {

     while (!isEmpty())

         dst.add(pop());

 }

여기서 입력 parameter 인 dst 는 Stack 클래스의 popAll 의 자료를 소비하는 것 (consumer) 이기 때문에,

super 가 사용됨을 알 수 있다. CS (consumer-super)


결론적으로, Java 언어에서 PECS 라는 것은 producer-extends, consumer-super 를 상징한다.


참고자료: Effective Java 2nd Edition, Joshua Bloch, Item 28