Java Academy · Lektion

PECS-princippet

Producer Extends, Consumer Super

Lektion 3 af 413 trin

PECS-princippet er en gratis Java Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Java Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Java Academy-kurset indeholder 4 lektioner i alt.

Hvad PECS står for

PECS betyder Producenten bruger extends, forbrugeren bruger super. Det er en huskeregel fra Joshua Blochs Effective Java.

  • Hvis en parameter producerer T (du læser fra den), skal du bruge ? extends T.
  • Hvis en parameter forbruger T (du skriver til den), skal du bruge ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

Den kanoniske copy-metode

Det klassiske PECS-eksempel er en kopimetode. Kilden producerer, så den er ? extends T. Destinationen forbruger, så den er ? 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);
    }
}

Hvorfor begge jokertegn giver fleksibilitet

Takket være PECS fungerer copy med mange typekombinationer: en Integer-kilde til en Number- eller Object-destination og en String-kilde til en CharSequence-destination.

Uden jokertegn skulle typerne matche præcist, hvilket ville begrænse genbrug kraftigt.

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

Et eksempel på en producent

En metode, der kun læser fra sin samling, skal bruge extends. Her summerer 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)));
    }
}

Et eksempel på en forbruger

En metode, der kun skriver til sin samling, skal bruge super. Her indsætter vi værdier i en forbruger.

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

Begge roller: Brug en almindelig type

Hvis en parameter både er producent og forbruger, skal den ikke bruge et jokertegn.

En metode, der læser og skriver i den samme liste, har brug for den præcise type 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'et anvender PECS overalt. Collections.max tager en Comparator<? super T>, fordi sammenligneren forbruger T-værdier for at sammenligne 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

Mange funktionelle grænseflader følger PECS. En Consumer<? super T> kan acceptere en håndtering, der er skrevet til en bredere type.

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

Funktionsproducenter og -forbrugere

Function<? super T, ? extends R> er den fulde PECS-form: inddataene forbruges (super), og uddataene produceres (extends).

Det lader en transformation acceptere bredere inddata og returnere mere specifikke uddata.

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

Returtyper: Undgå jokertegn

Brug ikke jokertegn i returtyper. De tvinger kaldende kode til også at håndtere jokertegn, så kompleksiteten spredes.

Returnér en konkret type, og behold jokertegn på parametre, hvor de giver fleksibilitet.

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 komplet PECS-fletning

Hvis vi samler det hele: en fletning, der læser fra to producenter og skriver til én forbruger.

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

Hurtigt tjek

Anvend PECS-reglen.

Opsamling

Du har lært PECS-princippet:

  • Producent bruger extends: skrivebeskyttede parametre bruger ? extends T.
  • Forbruger bruger super: skriveparametre bruger ? super T.
  • Parametre med begge roller bruger en almindelig typeparameter.
  • Undgå jokertegn i returtyper.

Næste emne er at anvende disse idéer på praktisk API-design.

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

Lær Java med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
104
Lektioner
374

Ofte stillede spørgsmål

Er lektionen “PECS-princippet” gratis?

Ja — hele teksten til “PECS-princippet” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Java Academy-kurset, skal du opgradere til CoddyKit PRO. Java Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “PECS-princippet”?

Producer Extends, Consumer Super Du øver dig i Java Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Java Academy?

Der kræves ingen tidligere erfaring. Java Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “PECS-princippet”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Java Academy-lektion?

Ja. Alle Java Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Upper bounded wildcards
  2. Lower bounded wildcards
  3. PECS-princippet
  4. Wildcards i API'er
← Tilbage til Java Academy