펙스(PECS): Producer-extends, consumer-super
T가 생산자라면 <? extends T>를 사용하고, 소비자라면 <? super T>를 사용할 것이 문서의 코드 예에서는 사용자 기반 클래스와 이를 확장하는 두 가지 클래스인 Operator와 Customer가 있는 데이터 모델 사용한다
컬렉션의 관점에서 PECS 규칙을 적용해야 한다는 것을 이해하는 것이 중요하다
즉, 리스트를 반복해서 그 요소들을 처리한다면, 이 리스트는 로직의 생산자 역할을 할 것이다
public void sendEmails(List<User> users) {
for (User user : users) {
System.out.println("sending email to " + user);
}
}
Operator 리스트를 sendEmail 메소드에 사용한다고 가정하자
Operator 클래스는 User를 확장하므로, 간단한 메소드 호출일 뿐이라고 예상하지만 컴파일 에러가 발생한다

문제를 해결하기 위해 PECS 규칙에 따라 sendEmail 메소드를 업데이트한다
users 리스트는 이 로직의 생산자이므로 extends 키워드를 사용한다
public void sendEmailsFixed(List<? extends User> users) {
for (User user : users) {
System.out.println("sending email to " + user);
}
}
따라서 이제 User 클래스로부터 상속하기만 하면 모든 제네릭 타입의 리스트에 대한 메소드를 쉽게 호출할 수 있다
List<Operator> operators = Arrays.asList(new Operator("sam"), new Operator("daniel"));
List<Customer> customers = Arrays.asList(new Customer("john"), new Customer("arys"));
sendEmailsFixed(operators);
sendEmailsFixed(customers);
컬렉션에 요소를 추가할 때 우리는 생산자가 되고, 리스트는 소비자의 역할을 하게 된다
Operator 리스트를 받고, 여기에 두 가지 요소를 추가하는 메소드를 작성해보자
private void addUsersFromMarketingDepartment(List<Operator> users) {
users.add(new Operator("john doe"));
users.add(new Operator("jane doe"));
}
Operator 리스트를 전달하면 완벽하게 작동한다
하지만 이를 사용하여 두 Operator를 Users 리스트에 추가하면 다시 컴파일 에러가 발생한다

따라서 메소드를 업데이트하고 super 키워드를 사용하여 Operator 또는 그 이전의 컬렉션을 허용하도록 해야 한다
private void addUsersFromMarketingDepartmentFixed(List<? super Operator> users) {
users.add(new Operator("john doe"));
users.add(new Operator("jane doe"));
}
로직이 컬렉션을 읽고 써야 하는 경우가 있을 수 있다
이러한 경우 컬렉션은 생산자이면서 동시에 소비자가 될 것이다
이런 시나리오를 처리하는 유일한 방법은 키워드 없이 기본 클래스를 사용하는 것이다
동일한 컬렉션을 사용해서 읽고 쓰는 것은 Command-Query 분리 원칙을 위반하는 것이므로 피해야 한다
private void addUsersAndSendEmails(List<User> users) {
users.add(new Operator("john doe")); // command
for (User user : users) {
System.out.println("sending email to: " + user); // query
}
}
제네릭 컬렉션에서 아이템을 가져오는 경우에는 생산자로 간주되어 extends를 사용해야 한다
반면 컬렉션에 아이템을 추가하는 경우에는 소비자로 간주되어 super를 사용해야 한다
extends와는 달리 매개변수화된 유형에 관계없이 하위 타입을 유지할 수 있다동일한 컬렉션에서 아이템을 추가하고 가져온다면, 이것은 우리의 코드에 좋지 않은 신호가 될 수 있다는 것을 배웠다
https://www.baeldung.com/java-generics-pecs
https://copyprogramming.com/howto/java-generics-pecs