PECS-periaate
Producer Extends, Consumer Super.
PECS-periaate on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.
Mistä PECS muodostuu
PECS tarkoittaa Producer Extends, Consumer Super. Se on Joshua Blochin Effective Java -kirjasta peräisin oleva muistisääntö.
- Jos parametri tuottaa T-tyyppisiä arvoja eli luette siitä, käyttäkää
? extends T-rajausta. - Jos parametri kuluttaa T-tyyppisiä arvoja eli kirjoitatte siihen, käyttäkää
? super T-rajausta.
public class Main {
public static void main(String[] args) {
System.out.println("Producer Extends, Consumer Super");
}
}Kanoninen copy-metodi
Klassinen PECS-esimerkki on copy-metodi. Lähde tuottaa arvoja, joten sen tyyppi on ? extends T. Kohde kuluttaa arvoja, joten sen tyyppi on ? 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);
}
}Miksi molemmat jokerityypit lisäävät joustavuutta
PECS:n ansiosta copy toimii monilla tyyppiyhdistelmillä: Integer-lähde voidaan kopioida Number- tai Object-kohteeseen ja String-lähde CharSequence-kohteeseen.
Ilman jokerityyppejä tyyppien pitäisi täsmätä täsmälleen, mikä rajoittaisi uudelleenkäyttöä merkittävästi.
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);
}
}Esimerkki tuottajasta
Kokoelmastaan vain lukeva metodi käyttää extends-rajausta. Tässä laskemme tuottajan arvot yhteen.
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)));
}
}Esimerkki kuluttajasta
Kokoelmaansa vain kirjoittava metodi käyttää super-rajausta. Tässä työnnämme arvoja kuluttajaan.
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);
}
}Molemmat roolit: käyttäkää tavallista tyyppiä
Jos parametri toimii sekä tuottajana että kuluttajana, siinä ei pidä käyttää jokerityyppiä.
Metodi, joka lukee ja kirjoittaa samaa listaa, tarvitsee täsmällisen T-tyypin.
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 Comparatorissa
JDK soveltaa PECS-periaatetta laajasti. Collections.max käyttää tyyppiä Comparator<? super T>, koska vertailija kuluttaa T-arvoja vertaillessaan niitä.
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 Stream.collect-metodissa
Monet funktionaaliset rajapinnat noudattavat PECS-periaatetta. Consumer<? super T> voi hyväksyä käsittelijän, joka on kirjoitettu laajempaa tyyppiä varten.
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>
}
}Functionin tuottajat ja kuluttajat
Function<? super T, ? extends R> on PECS:n täydellinen muoto: syöte kulutetaan (super) ja tulos tuotetaan (extends).
Tämän ansiosta muunnos voi hyväksyä laajempia syötteitä ja palauttaa suppeampia tuloksia.
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"));
}
}Palautustyypit: välttäkää jokerityyppejä
Älkää käyttäkö jokerityyppejä palautustyypeissä. Ne pakottavat myös kutsujat käsittelemään jokerityyppejä ja vuotavat siten monimutkaisuuden rajapinnan käyttäjille.
Palauttakaa konkreettinen tyyppi ja käyttäkää jokerityyppejä parametreissa silloin, kun ne lisäävät joustavuutta.
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)));
}
}PECS:n täydellinen yhdistelmä
Kun kaikki yhdistetään: yhdistämisoperaatio lukee kahdesta tuottajasta ja kirjoittaa yhteen kuluttajaan.
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);
}
}Pikatarkistus
Soveltakaa PECS-sääntöä.
Kertaus
Opitte PECS-periaatteen:
- Producer Extends: vain lukuun käytettävissä parametreissa käytetään
? extends T-rajausta. - Consumer Super: vain kirjoitukseen käytettävissä parametreissa käytetään
? super T-rajausta. - Molemmissa rooleissa käytetään tavallista tyyppiparametria.
- Välttäkää jokerityyppejä palautustyypeissä.
Seuraavaksi sovellamme näitä ajatuksia todellisten rajapintojen suunnitteluun.
public class Main {
public static void main(String[] args) {
System.out.println("PECS recap: Producer Extends, Consumer Super");
}
}Opi Java tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 104
- Oppitunnit
- 374
Usein kysytyt kysymykset
Onko oppitunti ”PECS-periaate” ilmainen?
Kyllä – oppitunnin ”PECS-periaate” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”PECS-periaate”?
Producer Extends, Consumer Super. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.
Kuinka kauan ”PECS-periaate”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?
Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.