0Pricing
Java Academy · 강의

PECS 원칙

생산자는 Extends, 소비자는 Super입니다

PECS 원칙은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

PECS가 의미하는 것

PECS는 “생산자는 상한, 소비자는 하한”을 의미합니다. Joshua Bloch의 『Effective Java』에서 소개된 기억법입니다.

  • 매개변수가 T를 생산한다면(매개변수에서 읽는다면) ? extends T를 사용합니다.
  • 매개변수가 T를 소비한다면(매개변수에 쓴다면) ? super T를 사용합니다.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

전형적인 copy 메서드

대표적인 PECS 예제는 copy 메서드입니다. 원본은 생산자이므로 ? extends T를 사용합니다. 대상은 소비자이므로 ? super T를 사용합니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Integer> src = List.of(1, 2, 3);
        List<Object> dest = new ArrayList<>();
        copy(src, dest);
        System.out.println(dest);
    }
}

두 와일드카드가 유연성을 높이는 이유

PECS 덕분에 copy는 다양한 형식 조합에서 작동합니다. 예를 들어 Integer 원본을 Number 또는 Object 대상에 복사할 수 있고, String 원본을 CharSequence 대상에 복사할 수 있습니다.

와일드카드가 없다면 형식이 정확히 일치해야 하므로 재사용성이 크게 떨어집니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Integer> ints = List.of(10, 20);
        List<Number> nums = new ArrayList<>();
        copy(ints, nums);
        System.out.println(nums);
    }
}

생산자 예제

컬렉션에서 읽기만 하는 메서드는 extends를 사용해야 합니다. 여기서는 생산자의 값을 합산합니다.

import java.util.List;

public class Main {
    static double sum(List<? extends Number> producer) {
        double total = 0;
        for (Number n : producer) total += n.doubleValue();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(sum(List.of(1, 2, 3)));
        System.out.println(sum(List.of(1.1, 2.2)));
    }
}

소비자 예제

컬렉션에 쓰기만 하는 메서드는 super를 사용해야 합니다. 여기서는 소비자에게 값을 넣습니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static void pushDigits(List<? super Integer> consumer) {
        for (int i = 0; i < 5; i++) consumer.add(i);
    }
    public static void main(String[] args) {
        List<Number> nums = new ArrayList<>();
        pushDigits(nums);
        System.out.println(nums);
    }
}

두 역할을 모두 맡는 경우: 일반 형식 사용

매개변수가 생산자이면서 소비자라면 와일드카드를 사용하지 않아야 합니다.

같은 리스트에서 읽고 쓰는 메서드에는 정확한 형식 T가 필요합니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void rotate(List<T> list) {
        if (list.isEmpty()) return;
        T last = list.remove(list.size() - 1);
        list.add(0, last);
    }
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
        rotate(list);
        System.out.println(list);
    }
}

Comparator에서의 PECS

JDK는 PECS를 광범위하게 적용합니다. Collections.max는 Comparator<? super T>를 받습니다. 비교기가 비교를 위해 T 값을 소비하기 때문입니다.

import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Comparator<Object> byHash = Comparator.comparingInt(Object::hashCode);
        List<String> words = List.of("a", "bb", "ccc");
        String max = java.util.Collections.max(words, byHash);
        System.out.println(max);
    }
}

Stream.collect에서의 PECS

많은 함수형 인터페이스가 PECS를 따릅니다. Consumer<? super T>는 더 넓은 형식을 대상으로 작성된 처리기도 받아들일 수 있습니다.

import java.util.List;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<Object> printer = o -> System.out.println("got: " + o);
        List<String> items = List.of("x", "y");
        items.forEach(printer); // forEach takes Consumer<? super T>
    }
}

Function의 생산자와 소비자

Function<? super T, ? extends R>는 PECS의 전체 형태입니다. 입력은 소비되고(super), 출력은 생산됩니다(extends).

따라서 이 변환은 더 넓은 입력을 받아 더 좁은 출력을 반환할 수 있습니다.

import java.util.function.Function;

public class Main {
    static <T, R> R apply(Function<? super T, ? extends R> fn, T value) {
        return fn.apply(value);
    }
    public static void main(String[] args) {
        Function<Object, Integer> len = o -> o.toString().length();
        System.out.println(apply(len, "hello"));
    }
}

반환 형식에서는 와일드카드 피하기

반환 형식에는 와일드카드를 사용하지 마십시오. 호출자도 와일드카드를 처리해야 하므로 복잡성이 밖으로 드러납니다.

구체적인 형식을 반환하고, 유연성이 필요한 매개변수에만 와일드카드를 사용하십시오.

import java.util.ArrayList;
import java.util.List;

public class Main {
    // Good: concrete return type
    static List<Number> doubled(List<? extends Number> src) {
        List<Number> out = new ArrayList<>();
        for (Number n : src) out.add(n.doubleValue() * 2);
        return out;
    }
    public static void main(String[] args) {
        System.out.println(doubled(List.of(1, 2, 3)));
    }
}

완전한 PECS 병합

지금까지의 내용을 합치면, 두 생산자에서 읽어 하나의 소비자에 병합하는 작업이 됩니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void merge(List<? extends T> a, List<? extends T> b, List<? super T> out) {
        for (T t : a) out.add(t);
        for (T t : b) out.add(t);
    }
    public static void main(String[] args) {
        List<Integer> a = List.of(1, 2);
        List<Integer> b = List.of(3, 4);
        List<Number> out = new ArrayList<>();
        merge(a, b, out);
        System.out.println(out);
    }
}

빠른 확인

PECS 규칙을 적용해 보십시오.

복습

PECS 원칙을 배웠습니다.

  • 생산자는 상한: 읽기 전용 매개변수에는 ? extends T를 사용합니다.
  • 소비자는 하한: 쓰기 전용 매개변수에는 ? super T를 사용합니다.
  • 두 역할을 모두 맡는 매개변수에는 일반 형식 매개변수를 사용합니다.
  • 반환 형식에는 와일드카드를 사용하지 마십시오.

다음에는 이러한 개념을 실제 API 설계에 적용합니다.

public class Main {
    public static void main(String[] args) {
        System.out.println("PECS recap: Producer Extends, Consumer Super");
    }
}

자주 묻는 질문

“PECS 원칙” 강의는 무료인가요?

네 — “PECS 원칙” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“PECS 원칙”에서 뭘 배우나요?

생산자는 Extends, 소비자는 Super입니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“PECS 원칙” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 상한 와일드카드
  2. 하한 와일드카드
  3. PECS 원칙
  4. API에서 와일드카드 사용
← Java Academy(으)로 돌아가기