PECS (Producer Extends, Consumer Super)

구창회·2023년 5월 29일
0

자바 공부 중

목록 보기
6/10

자바를 공부하다가 요런 코드를 보게 되었다.

Class<? extends Integer> aClass

?? 제너릭에 들어가 있는 저건 뭐지?

찾아보다 도움 받은 스택오버플로우의 링크를 아래에 먼저 남긴다
스택오버플로우


다시 위의 코드를 보자면 쉽게 말해, 클래스의 타입으로 올 수 있는 것은 Integer 클래스 자신 혹은 자식 클래스 라는 의미를 갖는다.

비슷한 코드로

Class<? super Integer> aClass

가 있으며

이는 클래스의 타입으로 올 수 있는 것은 Integer 클래스 자신 혹은 부모 클래스라는 제너릭 타입 제한을 말한다.


그렇다면 제목의 PECS 는 무슨 의미 일까?

그림을 곁들인 자세한 설명은 위의 스택오버플로우 글에 들어가면 있다.

위의 스택오버플로우 글의 질문은 이거였다.

I used to use List<? extends T>, but it does not allow me to add elements to it list.add(e), whereas the List<? super T> does.

쉽게 말해, 제너릭 안에 extends 가 들어간 타입 제한을 걸었을 때는 add 메소드가 동작하지 않았지만 super 키워드가 들어갔을때는 add 메소드가 동작했는데 왜 그런건가요? 라는 질문이다.


언뜻보기엔 둘다 제너릭으로 T 타입과 함께 허용할 클래스를 자식클래스냐 부모클래스냐 제한하는 것이기 때문에, T 타입인 데이터 e 를 집어넣는 메소드가 한쪽에선 동작하지 않는다는 것이 이해가 가지 않는다.

다시 말하지만 그림을 곁들인 자세한 설명은 위의 링크를 타고 스택오버플로우로 여행을 가시길 바란다.

결론만 말하면

문제는 저 와일드 카드 ? 에 있었다.

스택오버플로우의 질문 글로 다시 돌아가보자면,

T 타입의 데이터 e 를 <? super T> 에서는 집어넣을 수 있는데 <? extends T> 에서는 넣을 수 없더라라는 질문의 글을 다시 보자면,

문제의 <? extends T> 의 제너릭의 경우 T 타입의 자식클래스 일 수 도 있기 때문에 add 코드가 동작하지 않았던 것이다.

class Human {}

class Man extends Human {}

class Woman extends Human{}
public static void Main(String[] args) {
	Human human = new Human();
    Man man = new Man();
    System.out.println(human instanceof Man); // false
    System.out.println(man instanceof Human); // true
}

Human 클래스의 객체는 Man 자식 클래스 타입이 아니라고 드러난다.

문제의 <? extends Human> 의 경우에 적용해보자면

Human 타입의 데이터 e 를 add 할 수 없었던 이유는

제너릭 안에 들어있는 저 와일드카드 ? 때문에,<> 안에 들어갈 타입이 Human 일 수 도 있지만 자식클래스 Man 일 수도 있기에 타입이 확실하지 않기 때문에 데이터를 집어넣을 수 없었던 것이다.

반면 <? super Human> 의 경우는 가능했는데 그 이유는

<> 안에 들어갈 타입이 Human 이거나 Human 의 부모클래스 일 수 있는데, Human 의 객체라면 모두 Human 의 부모클래스의 객체라고 볼 수 있기 때문에 타입이 안정되어서 add 메소드가 동작 가능했던 것이다.


위와 같은 경우를 잘 정의한 용어가 바로 이 글의 제목인 PECS (Producer Extends, Consumer Super) 이다.

  • "Producer Extends" - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List<? extends Integer>. But you cannot add to this list.

  • "Consumer Super" - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List<? super Integer>. But there are no guarantees what type of object you may read from this list.

  • If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List< Integer >.


스택오버플로우의 질문 글로 부터 정리하자면, T 타입의 데이터를 쓰고 싶다면 super를 읽고 싶다면 extends 를, 둘다 하고 싶다면 그냥 와일드 카드 없이 List 를 사용하면 될 것이다.

profile
백엔드 엔지니어 프로 지망생

0개의 댓글