0Pricing
Java Academy · Leçon

Principe PECS

Producer Extends, Consumer Super

Principe PECS est une leçon Java Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Java Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Java Academy comprend 4 leçons au total.

Ce que signifie PECS

PECS signifie producteur avec une borne supérieure, consommateur avec une borne inférieure. Il s’agit d’un moyen mnémotechnique tiré de l’ouvrage Java efficace de Joshua Bloch.

  • Si un paramètre produit T (vous le lisez), utilisez ? extends T.
  • Si un paramètre consomme T (vous y écrivez), utilisez ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

La méthode copy canonique

L’exemple classique de PECS est une méthode copy. La source produit, elle utilise donc ? extends T. La destination consomme, elle utilise donc ? 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);
    }
}

Pourquoi les deux caractères génériques offrent plus de souplesse

Grâce à PECS, copy fonctionne avec de nombreuses combinaisons de types : une source d’entiers vers une destination de nombres ou d’objets, et une source de String vers une destination de CharSequence.

Sans caractères génériques, les types devraient correspondre exactement, ce qui limiterait fortement la réutilisation.

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

Exemple de producteur

Une méthode qui ne fait que lire sa collection doit utiliser une borne supérieure. Ici, nous additionnons les valeurs d’un producteur.

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

Exemple de consommateur

Une méthode qui ne fait qu’écrire dans sa collection doit utiliser une borne inférieure. Ici, nous ajoutons des valeurs dans un consommateur.

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

Les deux rôles : utiliser un type ordinaire

Si un paramètre est à la fois un producteur et un consommateur, il ne doit pas utiliser de caractère générique.

Une méthode qui lit et écrit dans la même liste a besoin du type exact 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 avec un comparateur

Le JDK applique PECS partout. Collections.max accepte un Comparator<? super T>, car le comparateur consomme les valeurs de type T pour les comparer.

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 avec Stream.collect

De nombreuses interfaces fonctionnelles suivent PECS. Un Consumer<? super T> peut accepter un gestionnaire écrit pour un type plus général.

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

Producteurs et consommateurs de fonctions

Function<? super T, ? extends R> est la forme complète de PECS : l’entrée est consommée (borne inférieure), et la sortie est produite (borne supérieure).

Cela permet à une transformation d’accepter des entrées plus générales et de renvoyer des sorties plus précises.

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

Types de retour : éviter les caractères génériques

N’utilisez pas de caractères génériques dans les types de retour. Ils obligent également les appelants à les gérer, ce qui leur impose une complexité inutile.

Renvoyez un type concret et gardez les caractères génériques sur les paramètres lorsqu’ils apportent de la souplesse.

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

Une fusion PECS complète

En rassemblant tout : une opération merge qui lit depuis deux producteurs pour alimenter un consommateur.

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

Vérification rapide

Appliquez la règle PECS.

Récapitulatif

Vous avez appris le principe PECS :

  • Producteur avec une borne supérieure : les paramètres en lecture seule utilisent ? extends T.
  • Consommateur avec une borne inférieure : les paramètres en écriture seule utilisent ? super T.
  • Les paramètres qui jouent les deux rôles utilisent un paramètre de type ordinaire.
  • Évitez les caractères génériques dans les types de retour.

Ensuite, nous appliquerons ces idées à la conception d’API réelles.

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

Questions Fréquemment Posées

La leçon « Principe PECS » est-elle gratuite ?

Oui — le texte complet de « Principe PECS » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Java Academy, passe à CoddyKit PRO. Le cours Java Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Principe PECS » ?

Producer Extends, Consumer Super Tu pratiques Java Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Java Academy ?

Aucune expérience préalable n'est requise. Java Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Principe PECS » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Java Academy ?

Oui. Chaque leçon Java Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Jokers à borne supérieure
  2. Jokers à borne inférieure
  3. Principe PECS
  4. Jokers dans les API
← Retour à Java Academy