Java Academy · Lektion

PECS-principen

Producer Extends, Consumer Super.

Lektion 3 av 413 steg

PECS-principen är en gratis lektion i Java Academy på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Java Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Java Academy innehåller totalt 4 lektioner.

Vad PECS står för

PECS står för Producer Extends, Consumer Super. Det är en minnesregel från Joshua Blochs Effective Java.

  • Om en parameter producerar T (du läser från den) använder du ? extends T.
  • Om en parameter konsumerar T (du skriver till den) använder du ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

Den klassiska copy-metoden

Det klassiska PECS-exemplet är en copy-metod. Källan producerar, så den är ? extends T. Destinationen konsumerar, så den är ? 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);
    }
}

Varför båda wildcardsen ger flexibilitet

Tack vare PECS fungerar copy för många typkombinationer: en Integer-källa till en Number- eller Object-destination, och en String-källa till en CharSequence-destination.

Utan wildcards skulle typerna behöva matcha exakt, vilket kraftigt skulle begränsa återanvändningen.

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

Ett producentexempel

En metod som bara läser från sin samling bör använda extends. Här summerar vi en producent.

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

Ett konsumentexempel

En metod som bara skriver till sin samling bör använda super. Här skickar vi in värden i en konsument.

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

Båda rollerna: använd en vanlig typ

Om en parameter både är producent och konsument ska den inte använda ett wildcard.

En metod som läser och skriver i samma lista behöver den exakta typen 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 i Comparator

JDK tillämpar PECS överallt. Collections.max tar en Comparator<? super T> eftersom comparatorn konsumerar T-värden för att jämföra dem.

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

Många funktionella gränssnitt följer PECS. En Consumer<? super T> kan acceptera en hanterare som är skriven för en bredare typ.

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

Producenter och konsumenter med Function

Function<? super T, ? extends R> är den fullständiga PECS-formen: indata konsumeras (super) och utdata produceras (extends).

Detta gör att en transformering kan acceptera bredare indata och returnera utdata av en smalare typ.

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

Undvik wildcards i returtyper

Använd inte wildcards i returtyper. De tvingar anroparna att också hantera wildcards, vilket gör komplexiteten synlig utanför metoden.

Returnera en konkret typ och använd wildcards på parametrar där de ger flexibilitet.

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

En fullständig PECS-sammanslagning

Om vi sätter ihop allt: en sammanslagning som läser från två producenter och skriver till en konsument.

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

Snabbkontroll

Tillämpa PECS-regeln.

Sammanfattning

Du har lärt dig PECS-principen:

  • Producer Extends: skrivskyddade parametrar använder ? extends T.
  • Consumer Super: skrivbara parametrar använder ? super T.
  • Parametrar med båda rollerna använder en vanlig typparameter.
  • Undvik wildcards i returtyper.

Nästa steg är att tillämpa dessa idéer på verklig API-design.

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

Lär dig Java med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
104
Lektioner
374

Vanliga frågor

Är lektionen ”PECS-principen” gratis?

Ja – hela texten till ”PECS-principen” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Java Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Java Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”PECS-principen”?

Producer Extends, Consumer Super. Ni övar på Java Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Java Academy?

Du behöver inga förkunskaper. Utbildningen i Java Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”PECS-principen”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Java Academy-lektionen?

Ja. Varje Java Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Övre begränsade wildcards
  2. Undre begränsade wildcards
  3. PECS-principen
  4. Wildcards i API:er
← Tillbaka till Java Academy