0Pricing
Java Academy · Aula

O princípio PECS

Producer Extends, Consumer Super.

O princípio PECS é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

O significado de PECS

PECS significa Produtor com limite superior, consumidor com limite inferior. É um recurso mnemônico do livro Java eficaz, de Joshua Bloch.

  • Se um parâmetro produz valores de T (você lê a partir dele), use ? extends T.
  • Se um parâmetro consome valores de T (você escreve nele), use ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

O método copy clássico

O exemplo clássico de PECS é um método copy. A origem produz, portanto usa ? extends T. O destino consome, portanto usa ? 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);
    }
}

Por que os dois curingas aumentam a flexibilidade

Graças a PECS, copy funciona com muitas combinações de tipos: uma origem de inteiros em um destino de números ou objetos, e uma origem de String em um destino de CharSequence.

Sem curingas, os tipos teriam de ser exatamente iguais, o que prejudicaria a reutilização.

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);
    }
}

Um exemplo de produtor

Um método que apenas lê de sua coleção deve usar um limite superior. Aqui somamos os valores de um produtor.

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)));
    }
}

Um exemplo de consumidor

Um método que apenas escreve em sua coleção deve usar um limite inferior. Aqui inserimos valores em um consumidor.

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);
    }
}

As duas funções: use um tipo simples

Se um parâmetro atua como produtor e consumidor, ele não deve usar um curinga.

Um método que lê e escreve na mesma lista precisa do tipo exato 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);
    }
}

PECS em Comparator

O JDK aplica PECS de forma abrangente. Collections.max recebe um Comparator<? super T> porque o comparador consome valores de T para compará-los.

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);
    }
}

PECS em Stream.collect

Muitas interfaces funcionais seguem PECS. Um Consumer<? super T> pode aceitar um manipulador escrito para um tipo mais abrangente.

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>
    }
}

Produtores e consumidores de funções

Function<? super T, ? extends R> é a forma completa de PECS: a entrada é consumida (super), e a saída é produzida (extends).

Isso permite que uma transformação aceite entradas mais abrangentes e retorne saídas mais específicas.

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"));
    }
}

Tipos de retorno: evite curingas

Não use curingas em tipos de retorno. Eles obrigam os chamadores a lidar com curingas também, expondo essa complexidade.

Retorne um tipo concreto e mantenha os curingas nos parâmetros, onde eles aumentam a flexibilidade.

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)));
    }
}

Uma combinação completa de PECS

Reunindo tudo: uma operação merge que lê de dois produtores e grava em um consumidor.

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);
    }
}

Verificação rápida

Aplique a regra PECS.

Recapitulação

Você aprendeu o princípio PECS:

  • Produtor com limite superior: parâmetros somente para leitura usam ? extends T.
  • Consumidor com limite inferior: parâmetros somente para escrita usam ? super T.
  • Parâmetros com as duas funções usam um parâmetro de tipo simples.
  • Evite curingas em tipos de retorno.

A seguir, a aplicação dessas ideias ao design de APIs reais.

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

Perguntas Frequentes

A aula “O princípio PECS” é grátis?

Sim — o texto completo de “O princípio PECS” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “O princípio PECS”?

Producer Extends, Consumer Super. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “O princípio PECS”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Curingas com limite superior
  2. Curingas com limite inferior
  3. O princípio PECS
  4. Curingas em APIs
← Voltar para Java Academy