PECS-principen
Producer Extends, Consumer Super.
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");
}
}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.