Co to jest Pecs (producent rozszerza Consumer Super)?

Natknąłem się na PECS (skrót od Producent extends i konsument super) podczas czytania o generykach.

Może mi ktoś wyjaśnić jak używać PECS do rozwiązywania nieporozumień między extends i super?

Author: Lii, 2010-04-27

11 answers

Tl; dr: "PECS" jest z punktu widzenia kolekcji. Jeśli jesteś tylko wyciągając przedmioty z kolekcji generycznej, jest to producent i powinieneś używać extends; jeśli jesteś tylko wypychając przedmioty, jest to konsument i powinieneś używać super. Jeśli robisz oba z tą samą kolekcją, nie powinieneś używać extends ani super.


Załóżmy, że masz metodę, która przyjmuje jako swój parametr zbiór rzeczy, ale chcesz, aby była bardziej elastyczna niż po prostu akceptuję Collection<Thing>.

Przypadek 1: chcesz przejść przez kolekcję i robić rzeczy z każdym przedmiotem.
Następnie lista jest producentem , więc powinieneś użyć Collection<? extends Thing>.

Rozumowanie jest takie, że {[5] } może posiadać dowolny Podtyp Thing, a zatem każdy element będzie zachowywał się jak Thing podczas wykonywania operacji. (W rzeczywistości nie można nic dodać do Collection<? extends Thing>, ponieważ nie można wiedzieć w czasie wykonywania, który specyficzny Podtyp Thing kolekcji trzyma.)

Przypadek 2: chcesz dodać rzeczy do kolekcji.
Następnie lista jest konsument , więc należy użyć Collection<? super Thing>.

Rozumowanie tutaj jest takie, że w przeciwieństwie Collection<? extends Thing>, Collection<? super Thing> może zawsze posiadać Thing bez względu na rzeczywisty parametryzowany Typ. Tutaj nie obchodzi cię, co jest już na liście, o ile pozwoli na dodanie Thing; to gwarantuje ? super Thing.

 654
Author: Michael Myers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-29 16:39:36

Zasady stojące za tym w informatyce są nazwane

  • Kowariancja - ? extends MyClass,
  • Kontrawariancja - ? super MyClass i
  • Niezmienniczość / nie-wariancja-MyClass

Poniższy rysunek powinien wyjaśnić tę koncepcję.

Zdjęcie dzięki uprzejmości: Andrey Tyukin

KOWARIANCJA vs Kontrawarancja

 453
Author: anoopelias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

PECS (skrót od " Producent extends i konsument super") Może być wyjaśnione przez : zasada Get and Put

W języku Java Można używać tylko jednego języka Java.]}

Stwierdza,

  1. użyj extends wildcard gdy tylko uzyskasz wartości ze struktury
  2. użyj super wildcard gdy tylko umieść wartości w strukturze
  3. i nie używaj wieloznaczności gdy zarówno dostać i umieścić.

Zrozummy to na przykładzie:

1. Dla Extends Wildcard(get values czyli Producent extends)

Jest to metoda, która pobiera zbiór liczb, zamienia każdą z nich na double i sumuje je
public static double sum(Collection<? extends Number> nums) {
   double s = 0.0;
   for (Number num : nums) 
      s += num.doubleValue();
   return s;
}

wywołajmy metodę:

List<Integer>ints = Arrays.asList(1,2,3);
assert sum(ints) == 6.0;
List<Double>doubles = Arrays.asList(2.78,3.14);
assert sum(doubles) == 5.92;
List<Number>nums = Arrays.<Number>asList(1,2,2.78,3.14);
assert sum(nums) == 8.92;

Od, sum() zastosowanie metodyextends, wszystkie poniższe połączenia są legalne. Pierwsze dwa wywołania nie byłyby legalne, gdyby nie używany.

wyjątek : Możesz Nie umieść niczego w typie zadeklarowanym z extends wildcard-z wyjątkiem wartości null, która należy do każdego typu odniesienia:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(null);  // ok
assert nums.toString().equals("[1, 2, null]");

2. Dla Super Wildcard(wpisz wartości tj. konsument super)

Oto metoda, która pobiera zbiór liczb i int n i umieszcza pierwsze n liczby całkowite, począwszy od zera, w kolekcja:

public static void count(Collection<? super Integer> ints, int n) {
    for (int i = 0; i < n; i++) ints.add(i);
}

wywołajmy metodę:

List<Integer>ints = new ArrayList<Integer>();
count(ints, 5);
assert ints.toString().equals("[0, 1, 2, 3, 4]");
List<Number>nums = new ArrayList<Number>();
count(nums, 5); nums.add(5.0);
assert nums.toString().equals("[0, 1, 2, 3, 4, 5.0]");
List<Object>objs = new ArrayList<Object>();
count(objs, 5); objs.add("five");
assert objs.toString().equals("[0, 1, 2, 3, 4, five]");

Od, count() zastosowanie metodysuper, wszystkie poniższe połączenia są legalne: Ostatnie dwa połączenia nie byłyby legalne, gdyby nie użyto super.

wyjątek : Możesz nie można uzyskać niczego Z Typu zadeklarowanego przezsuper wildcard-z wyjątkiem wartości typu Object, która jest supertyp każdego typu odniesienia:

List<Object> objs = Arrays.<Object>asList(1,"two");
List<? super Integer> ints = objs;
String str = "";
for (Object obj : ints) str += obj.toString();
assert str.equals("1two");

3. Gdy zarówno Get and Put, don ' t Use wildcard

Kiedykolwiek zarówno umieścić wartości w i uzyskać wartości z tej samej struktury, nie należy używać wieloznacznych.

public static double sumCount(Collection<Number> nums, int n) {
   count(nums, n);
   return sum(nums);
}
 132
Author: Prateek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-08 23:28:24
public class Test {

    public class A {}

    public class B extends A {}

    public class C extends B {}

    public void testCoVariance(List<? extends B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b); // does not compile
        myBlist.add(c); // does not compile
        A a = myBlist.get(0); 
    }

    public void testContraVariance(List<? super B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b);
        myBlist.add(c);
        A a = myBlist.get(0); // does not compile
    }
}
 27
Author: Gab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-07 18:15:13

PECS (producent extends i konsument super)

Mnemonic → zasada Get and Put.

Zasada ta stwierdza, że:

  • używaj wildcard extends, gdy otrzymujesz tylko wartości ze struktury.
  • używaj super wildcard, gdy umieszczasz tylko wartości w strukturze.
  • i nie używaj symboli wieloznacznych, kiedy dostajesz i wkładasz.

W Javie parameters i generic type parameters nie obsługują dziedziczenia jako / align = "left" /

class Super {
    void testCoVariance(Object parameter){} // method Consumes the Object
    Object testContraVariance(){ return null;} //method Produces the Object
}

class Sub extends Super {
    @Override
    void testCoVariance(String parameter){} //doesn't support eventhough String is subtype of Object

    @Override
    String testContraVariance(){ return null;} //compiles successfully i.e. return type is don't care 
}

Zasada substytucji Liskowa: Tablice są kowariantne(niebezpieczne), ale generyczne nie są niezmienne (bezpieczne). tzn. zasada substytucji nie działa z parametryzowanymi typami, co oznacza, że pisanie jest nielegalne.
Kowariantny oznacza po prostu, że jeśli X jest podtypem Y, to X[] będzie również podtypem Y[].

Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)

List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime  

Więcej przykładów

Ograniczony (tzn. zmierzający w kierunku gdzieś) wildcard : Istnieją 3 różne smaki wildcards:

  • in-variance/Non-variance: ? lub ? extends Object - Unbounded Wildcard. Oznacza rodzinę WSZYSTKICH rodzajów. Używaj, gdy dostajesz i wkładasz.
  • Współwariancja: ? extends T (rodzina WSZYSTKICH rodzajów będących podtypami T) - wieloznaczna z górną granicą . T jest górną - najwyższą klasą w hierarchii dziedziczenia. Użyj extends wildcard, gdy tylko otrzymasz wartości poza strukturą.
  • kontra-wariancja: ? super T (rodzina WSZYSTKICH rodzajów będących supertypami T) - wieloznaczna z dolną granicą . T jest niższą-najwyższą klasą w hierarchii dziedziczenia. Użyj super, gdy tylko umieścisz wartości w strukturze.

Uwaga: symbol wieloznaczny ? oznacza zero lub jeden raz , reprezentuje Nieznany typ. Symbol wieloznaczny może być użyty jako typ parametru, nigdy nie używany jako typ argument wywołania metody generycznej, utworzenie instancji klasy generycznej.(np. gdy używana jest wieloznaczna nazwa, która nie jest używana w innych programach, takich jak używamy T)

Tutaj wpisz opis obrazka

class Shape { void draw() {}}

class Circle extends Shape {void draw() {}}

class Square extends Shape {void draw() {}}

class Rectangle extends Shape {void draw() {}}

public class TestContraVariance {
 /*
   * Example for an upper bound wildcard (Get values i.e Producer `extends`)
   * 
   * */  

    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Shape()); // Error:  is not applicable for the arguments (Shape) i.e. inheritance is not supporting
        list.add(new Circle()); // Error:  is not applicable for the arguments (Circle) i.e. inheritance is not supporting
        list.add(new Square()); // Error:  is not applicable for the arguments (Square) i.e. inheritance is not supporting
        list.add(new Rectangle()); // Error:  is not applicable for the arguments (Rectangle) i.e. inheritance is not supporting
        Shape shape= list.get(0);//compiles so list act as produces only

        /*You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape> 
         * You can get an object and know that it will be an Shape
         */         
    }
      /* 
* Example for  a lower bound wildcard (Put values i.e Consumer`super`)
* */
    public void testContraVariance(List<? super Shape> list) {
        list.add(new Shape());//compiles i.e. inheritance is supporting
        list.add(new Circle());//compiles i.e. inheritance is  supporting
        list.add(new Square());//compiles i.e. inheritance is supporting
        list.add(new Rectangle());//compiles i.e. inheritance is supporting
        Shape shape= list.get(0); // Error: Type mismatch, so list acts only as consumer
        Object object= list.get(0); // gets an object, but we don't know what kind of Object it is.

        /*You can add a Shape,Circle,Square,Rectangle to a List<? extends Shape> 
        * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
        */  
    }
}

Generyki i przykłady

 25
Author: Premraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 01:08:19

Jak wyjaśniam w moja odpowiedź {[20] } na inne pytanie, PECS jest urządzeniem mnemonicznym stworzonym przez Josha Blocha, aby pomóc zapamiętaćP roducer extends, C onsumer super.

Oznacza to, że gdy parametryzowany Typ przekazywany do metody wytworzy instancje T (zostaną z niej w jakiś sposób pobrane), należy użyć ? extends T, ponieważ każda instancja podklasy T jest również T.

Gdy parametryzowany typ jest przekazanie do metody spowoduje, że pochłonie instancje T (zostaną przekazane do niej, aby coś zrobić), ? super T powinno być użyte, ponieważ instancja T może być legalnie przekazana do dowolnej metody, która akceptuje jakiś supertyp T. A Comparator<Number> może być używany na przykład na Collection<Integer>. ? extends T nie zadziała, ponieważ Comparator<Integer> nie może działać na Collection<Number>.

Należy pamiętać, że zazwyczaj należy używać tylko ? extends T i ? super T dla parametrów jakiejś metody. Metody powinny po prostu używać T jako parametr typu w ogólnym typie zwrotnym.

 20
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 12:34:45

W skrócie łatwe do zapamiętania PECS

  1. Użyj symbolu <? extends T>, jeśli chcesz odzyskać obiekt wpisz T ze zbioru.
  2. Użyj symbolu <? super T>, jeśli chcesz umieścić obiekty typu T w kolekcja.
  3. Jeśli chcesz zadowolić obie rzeczy, cóż, nie używaj żadnych symboli wieloznacznych. Jako to proste.
 16
Author: Pradeep Kr Kaushal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-13 06:51:24

(Dodawanie odpowiedzi, bo za mało przykładów z generykami)

       // Source 
       List<Integer> intList = Arrays.asList(1,2,3);
       List<Double> doubleList = Arrays.asList(2.78,3.14);
       List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);

       // Destination
       List<Integer> intList2 = new ArrayList<>();
       List<Double> doublesList2 = new ArrayList<>();
       List<Number> numList2 = new ArrayList<>();

        // Works
        copyElements1(intList,intList2);         // from int to int
        copyElements1(doubleList,doublesList2);  // from double to double


     static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
        for(T n : src){
            dest.add(n);
         }
      }


     // Let's try to copy intList to its supertype
     copyElements1(intList,numList2); // error, method signature just says "T"
                                      // and here the compiler is given 
                                      // two types: Integer and Number, 
                                      // so which one shall it be?

     // PECS to the rescue!
     copyElements2(intList,numList2);  // possible



    // copy Integer (? extends T) to its supertype (Number is super of Integer)
    private static <T> void copyElements2(Collection<? extends T> src, 
                                          Collection<? super T> dest) {
        for(T n : src){
            dest.add(n);
        }
    }
 4
Author: Andrejs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-17 18:12:22

Zapamiętaj to:

Konsument jeść Kolacja (super); Producent rozszerza jego fabryka rodzica

 2
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-29 06:13:16

Przyjmijmy tę hierarchię:

class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C

Wyjaśnijmy PE-producent rozszerza:

List<? extends Shark> sharks = new ArrayList<>();

Dlaczego nie można dodawać obiektów rozszerzających "Shark" na tej liście? like:

sharks.add(new HammerShark());//will result in compilation error

Ponieważ masz listę, która może być typu A, B lub C w trybie runtime, nie możesz dodać do niej żadnego obiektu typu A, B lub C, ponieważ możesz skończyć z kombinacją, która nie jest dozwolona w Javie.
w praktyce kompilator rzeczywiście widzi w czasie kompilacji, że dodajesz B:

sharks.add(new HammerShark());

...ale nie ma sposobu, aby stwierdzić, czy w czasie wykonywania, Twój B będzie Podtyp lub supertyp typu listy. W czasie wykonywania typem listy może być dowolny z typów A, B, C. Nie można więc na przykład dodać HammerSkark (super type) DO LISTY DeadHammerShark.

*powiesz: "OK, ale dlaczego nie mogę dodać HammerSkark w nim, ponieważ jest to najmniejszy Typ?". Odpowiedź: jest to najmniejszy , który znasz. Kup HammerSkark może być przedłużony też przez kogoś innego i kończy się w ten sam scenariusz.

Wyjaśnijmy CS-Consumer Super:

W tej samej hierarchii możemy spróbować tego:

List<? super Shark> sharks = new ArrayList<>();

Co i dlaczego Możesz dodać do tej listy?

sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());

Możesz dodać powyższe typy obiektów, ponieważ wszystko poniżej shark (A,B,C) zawsze będzie podtypem wszystkiego powyżej shark (X, Y, Z). Łatwe do zrozumienia.

Ty nie możesz dodawać typów powyżej rekina, ponieważ w czasie wykonywania typ dodawanego obiektu może być wyższy w hierarchii niż deklarowany typ listy (X, Y,Z). To nie jest dozwolone.

Ale dlaczego nie możesz czytać z tej listy? (Chodzi mi o to, że można wyciągnąć z niego element, ale nie można go przypisać do niczego innego niż obiekt o): {]}

Object o;
o = sharks.get(2);// only assignment that works

Animal s;
s = sharks.get(2);//doen't work

W czasie wykonywania, typem listy może być dowolny typ powyżej A: X, Y, Z,... Kompilator może skompilować instrukcję assignment (co wydaje się poprawne), ale w czasie wykonywania typ S (zwierzę) może być niższy w hierarchii niż deklarowany Typ lista (która może być kreatura, lub wyższa). To nie jest dozwolone.

Podsumowując

Używamy <? super T> do dodawania obiektów typu równego lub poniżej T w liście. nie możemy czytać z to.
używamy <? extends T> do odczytywania obiektów typu równego lub poniżej T z listy. nie możemy dodać do niego elementu.

 1
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-23 17:42:34

Symbole wieloznaczne mogą być używane na trzy sposoby:

              - Upper bound Wildcard  ( ? extends Type ).

              - Lower bound Wildcard  ( ? super Type ) .

              - Unbounded Wildcard    ( ? ) .

Dla celów tej dyskusji pomocne jest myślenie o zmiennych jako o dostarczeniu jednej z dwóch funkcji:

                      - In Variable

                              An "in" variable serves up data to the code. 
                              Imagine a copy method with two arguments: 
                                      copy(src, dest)
                              The src argument provides the data to be copied, so it is the "in" parameter.
                      - Out Variable

                              An "out" variable holds data for use elsewhere. In the copy example, 
                                      copy(src, dest)
                              the dest argument accepts data, so it is the "out" parameter.

              An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
              An "out" variable is defined with a lower bounded wildcard, using the super keyword.
              In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
              In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.

                      class NaturalNumber 
                      {

                              private int i;

                              public NaturalNumber(int i)
                              { 
                                      this.i = i;
                              }
                      }

                          class EvenNumber extends NaturalNumber 
                          {

                              public EvenNumber(int i) 
                              { 
                                      super(i);
                              }
                          }

              Consider the following code:

                      List<EvenNumber> le = new ArrayList<>();
                      List<? extends NaturalNumber> ln = le;
                      ln.add(new NaturalNumber(35));  // compile-time error


                      You can add null.
                      You can invoke clear.
                      You can get the iterator and invoke remove.
                      You can capture the wildcard and write elements that you've read from the list.
 0
Author: Ali Yeganeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-01 13:12:23