0Pricing
Java Academy · Lezione

Il principio PECS

Producer Extends, Consumer Super

Il principio PECS è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Il significato di PECS

PECS significa Producer Extends, Consumer Super. È un mnemonico tratto da Effective Java di Joshua Bloch.

  • Se un parametro produce T (si legge da esso), utilizzi ? extends T.
  • Se un parametro consuma T (si scrive al suo interno), utilizzi ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

Il metodo copy canonico

L'esempio classico di PECS è un metodo di copia. La sorgente produce, quindi usa ? extends T. La destinazione consuma, quindi 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);
    }
}

Perché entrambe le wildcard aumentano la flessibilità

Grazie a PECS, copy funziona con molte combinazioni di tipi: una sorgente Integer verso una destinazione Number o Object, oppure una sorgente String verso una destinazione CharSequence.

Senza le wildcard, i tipi dovrebbero corrispondere esattamente, limitando gravemente il riutilizzo.

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

Un esempio di produttore

Un metodo che legge soltanto dalla propria raccolta dovrebbe usare extends. Qui sommiamo i valori di un produttore.

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

Un esempio di consumatore

Un metodo che scrive soltanto nella propria raccolta dovrebbe usare super. Qui inseriamo valori in un consumatore.

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

Entrambi i ruoli: utilizzare un tipo semplice

Se un parametro è sia produttore sia consumatore, non dovrebbe usare una wildcard.

Un metodo che legge e scrive nella stessa lista richiede il tipo esatto 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 in Comparator

Il JDK applica PECS in modo sistematico. Collections.max accetta un Comparator<? super T> perché il comparatore consuma valori T per confrontarli.

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

Molte interfacce funzionali seguono PECS. Un Consumer<? super T> può accettare un gestore scritto per un tipo più generale.

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

Produttori e consumatori di Function

Function<? super T, ? extends R> è la forma completa di PECS: l'input viene consumato (super), mentre l'output viene prodotto (extends).

Questo consente a una trasformazione di accettare input più generali e restituire output più specifici.

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

Tipi restituiti: evitare le wildcard

Non utilizzi wildcard nei tipi restituiti. Costringono anche chi chiama il metodo a gestire le wildcard, esponendo una complessità inutile.

Restituisca un tipo concreto e mantenga le wildcard nei parametri, dove aumentano la flessibilità.

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

Un'unione PECS completa

Riuniamo ora i concetti: un'operazione di unione che legge da due produttori e scrive in un consumatore.

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 rapida

Applichi la regola PECS.

Riepilogo

Ha imparato il principio PECS:

  • Producer Extends: i parametri di sola lettura usano ? extends T.
  • Consumer Super: i parametri di sola scrittura usano ? super T.
  • I parametri con entrambi i ruoli usano un parametro di tipo semplice.
  • Eviti le wildcard nei tipi restituiti.

Ora applicheremo queste idee alla progettazione di API reali.

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

Domande Frequenti

La lezione «Il principio PECS» è gratuita?

Sì — il testo completo di «Il principio PECS» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Il principio PECS»?

Producer Extends, Consumer Super Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Il principio PECS»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Wildcard con limite superiore
  2. Wildcard con limite inferiore
  3. Il principio PECS
  4. Wildcard nelle API
← Torna a Java Academy