Frage

Ich kam in PECS (kurz für Producer extends und Verbraucher super ), während auf Generika zu lesen auf.

Kann mir jemand erklären, wie PECS verwenden, um Entschlossenheit Verwirrung zwischen extends und super?

War es hilfreich?

Lösung

tl; dr: "PECS" ist aus der Sicht der Sammlung. Wenn Sie nur Elemente aus einer generischen Sammlung ziehen, ist es ein Produzent und Sie sollten extends verwenden; wenn Sie nur Füllung Artikel in, ist es ein Verbraucher und Sie sollten super verwenden. Wenn Sie mit der gleichen Sammlung beides tun, sollten Sie keine extends oder super verwenden.


Angenommen, Sie haben eine Methode, die eine Sammlung von Dingen als Parameter nimmt, aber Sie wollen es, flexibler zu sein als nur ein Collection<Thing> akzeptieren.

Fall 1:. Sie wollen durch die Sammlung gehen und Dinge tun, mit jedem Element
Dann wird die Liste ist ein Hersteller , so dass Sie einen Collection<? extends Thing> verwenden sollten.

Die Begründung ist, dass ein Collection<? extends Thing> eines Subtyps von Thing halten konnte, und damit jedes Element wird als Thing verhalten, wenn Sie Ihre Operation durchführen. (Sie können eigentlich nichts zu einem Collection<? extends Thing> hinzufügen, weil Sie zur Laufzeit nicht wissen können, die spezifischen Subtyp von Thing der Sammlung enthält.)

Fall 2: Sie wollen die Dinge in die Sammlung hinzuzufügen
. Dann wird die Liste ist ein Verbraucher , so dass Sie einen Collection<? super Thing> verwenden sollten.

Die Argumentation hier ist, dass im Gegensatz zu Collection<? extends Thing>, Collection<? super Thing> kann immer ein Thing halten, egal, was der tatsächliche parametrisierte Typ ist. Hier kümmert es dich nicht, was bereits in der Liste enthalten ist, solange es ein Thing ermöglichen wird, hinzugefügt werden; das ist, was ? super Thing garantiert.

Andere Tipps

Die Prinzipien hinter diesem in der Informatik heißen

  • Kovarianz: ? extends MyClass,
  • Kontra: ? super MyClass und
  • Invariance / nicht-Varianz: MyClass

Das Bild unten soll das Konzept erläutern. Foto mit freundlicher Genehmigung: Andrey Tyukin

Kovarianz vs Kontra

PECS (Produzent extends und Verbraucher super)

mnemonic ? Get und Put-Prinzip.

Dieses Prinzip besagt, dass:

  • Verwenden Sie ein erweitert Platzhalter, wenn Sie nur Werte erhalten aus einer Struktur.
  • Verwenden Sie ein Super-Platzhalter, wenn Sie nur Werte in eine Struktur bringen.
  • Und nicht einen Platzhalter verwenden, wenn Sie beide get und put.

Beispiel in Java:

class Super {

    Object testCoVariance(){ return null;} //Covariance of return types in the subtype.
    void testContraVariance(Object parameter){} // Contravariance of method arguments in the subtype.
}

class Sub extends Super {

    @Override
    String testCoVariance(){ return null;} //compiles successfully i.e. return type is don't care(String is subtype of Object) 
    @Override
    void testContraVariance(String parameter){} //doesn't support even though String is subtype of Object

}

Liskov Substitutionsprinzip: wenn S ein Subtyp von T ist, dann Objekte vom Typ T können mit Objekten des Typs S ersetzt werden

Im Rahmen des Typsystem einer Programmiersprache, eine Typisierung Regel

  • covariant , wenn es die Reihenfolge der Arten bewahrt (=), die Aufträge Arten von spezifischer zu allgemeineren;
  • kontra , wenn es diese Reihenfolge umkehrt;
  • invariant oder nicht-variante, wenn beides nicht zutrifft.

Kovarianz und Kontra

  • Read-only-Datentypen (Quellen) covariant ;
  • Nur-Schreib-Datentypen (Senken) sein kontra .
  • Mutable Datentypen, die sowohl als Quellen und Senken handeln sollte invariant .

Um dieses allgemeine Phänomen veranschaulichen, betrachten die Array-Typ. Für den Typ Tier können wir die Art Tier machen []

  • covariant : eine Katze [] ist ein Tier [];
  • kontra : ein Tier [] ist eine Katze [];
  • invariant . Ein Tier [] ist keine Katze [] und eine Katze [] ist nicht ein Tier []

Java Beispiele:

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  

weitere Beispiele

beschränkter (d Überschrift in Richtung irgendwo.) Platzhalter : Es gibt 3 verschiedene Geschmacksrichtungen von Wildcards:

  • In-Varianz / Non-Varianz: ? oder ? extends Object - Unbounded Wildcard. Es steht für die Familie aller Art. Verwenden Sie, wenn Sie beide get und put.
  • Co-Varianz: ? extends T (die Familie aller Art, die Subtypen von T sind) - ein Platzhalter mit einem Obergrenze . T ist die oberen -Most Klasse in der Vererbungshierarchie. Verwenden Sie einen extends Platzhalter, wenn Sie nur Get eine Strukturwert aus.
  • Contra-Varianz: ? super T (die Familie aller Art, die übergeordneten Typen von T sind) - ein Platzhalter mit einem unteren gebunden. T ist die untere -Most Klasse in der Vererbungshierarchie. Verwenden Sie ein super Platzhalter, wenn Sie nur Setzen Sie Werte in eine Struktur.

Hinweis: Platzhalter ? Mittel Null oder eine Zeit , stellt einen unbekannten Typ. Der Platzhalter kann als Typ eines Parameter verwendet werden, niemals als eine Art Argument für einen generischen Methodenaufruf, eine generische Klasseninstanz Erstellung verwendet. (Dh wenn gebrauchter Wildcard dass Verweis in an anderer Stelle im Programm nicht verwendet wie wir T verwenden)

 image description hier

eingeben
class Shape { void draw() {}}

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

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

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

public class Test {
 /*
   * 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<? super Shape> 
        * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
        */  
    }
}

Generika und Beispiele

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

Wie erkläre ich in meine Antwort eine andere Frage, PECS ist eine Gedächtnisstütze von Josh Bloch um Hilfe erinnern P roducer extends, C onsumer super.

erstellt

Das bedeutet, dass, wenn eine parametrisierte Typ eine Methode übergeben wird, wird produzieren Instanzen T (sie werden in irgendeiner Weise von ihm abgerufen werden), ? extends T verwendet werden sollte, da jede Instanz einer Unterklasse von T ist auch ein T.

Wenn eine parametrisierte Typ eine Methode übergeben wird, wird consume Instanzen T (sie werden an sie übergeben werden, etwas zu tun), ? super T verwendet werden sollte, da eine Instanz T rechtlich übergeben werden können, um Jedes Verfahren, das einen übergeordneten Typen von T annimmt. Ein Comparator<Number> könnte auf einem Collection<Integer> verwendet wird, zum Beispiel. ? extends T würde nicht funktionieren, weil ein Comparator<Integer> nicht auf einem Collection<Number> arbeiten könnte.

Beachten Sie, dass im Allgemeinen sollten Sie nur ? extends T und ? super T für die Parameter von einigen Verfahren werden. Methoden sollten nur T als Typ-Parameter auf einem generischen Rückgabetyp verwenden.

Auf den Punkt gebracht, drei einfache Regeln PECS erinnern:

  1. Verwenden Sie das <? extends T> Platzhalter, wenn Sie Aufgabe abrufen müssen Typ T aus einer Sammlung.
  2. die <? super T> Platzhalter verwenden, wenn Sie Objekte vom Typ T benötigen setzen in eine Sammlung.
  3. Wenn Sie beide Dinge erfüllen müssen, na ja, verwenden Sie keine Platzhalter. Wie einfach.

(Hinzufügen einer Antwort, weil nie genug Beispiele mit Generics Wildcards)

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

Nehmen wir an, diese Hierarchie:

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

Lassen Sie uns PE klären - Hersteller erweitert:

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

Warum Sie können keine Objekte hinzufügen, die „Shark“ in dieser Liste erweitern? wie:

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

Da haben Sie eine Liste, die vom Typ A, B oder C sein kann zur Laufzeit , können Sie kein Objekt vom Typ A, B oder C in sich hinzufügen, weil Sie mit einer Kombination können am Ende Das ist nicht in Java erlaubt.
In der Praxis kann der Compiler in der Tat bei compiletime sehen, dass Sie ein B hinzufügen:

sharks.add(new HammerShark());

... aber es hat keine Möglichkeit zu sagen, ob zur Laufzeit, Sie B einen Subtyp oder übergeordneten Typ von der Liste Typ sein. Zur Laufzeit kann die Liste Typ eine des Typs A, B, C, damit Sie nicht HammerSkark (Super-Typ) in einer Liste von DeadHammerShark zum Beispiel des Hinzufügen am Ende können.

* Sie sagen: „OK, aber warum kann ich nicht in sie fügen HammerSkark, da es die kleinste Art ist?“. Antwort: Es ist die kleinste Sie wissen. Aber HammerSkark kann jemand erweitert werden, sonst und man in dem gleichen Szenario enden.

Lassen Sie uns klären CS - Consumer Super-:

In der gleichen Hierarchie können wir dies versuchen:

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

Was und warum Sie können zu dieser Liste hinzufügen?

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

Sie können die oben genannten Arten von Objekten hinzufügen, weil alles unter Hai (A, B, C) immer Subtypen von etwas über Hai (X, Y, Z) sein. Einfach zu verstehen.

Sie kann nicht hinzufügen Typen über Hai, weil zur Laufzeit die Art des zugegebenen Objekt kann in der Hierarchie als die deklarierten Typ der Liste (X, Y, Z höher sein ). Dies ist nicht erlaubt.

Aber warum kann man nicht aus dieser Liste lesen? (Ich meine Sie ein Element aus ihm heraus zu bekommen, aber man kann es nicht auf etwas anderes als zuordnen Object o):

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

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

Zur Laufzeit kann die Art der Liste jeder Typ über A sein: X, Y, Z, ... Der Compiler kann Ihre Zuweisungsanweisung (die richtigen scheint), aber zur Laufzeit die Art des s (Tier) kann als der deklarierten Typ der Liste in der Hierarchie niedriger sein kompiliert (die Kreatur sein könnte, oder höher ). Dies ist nicht erlaubt.

Fazit

Wir verwenden <? super T> Objekte hinzufügen von Typen oder unter T in der Liste entsprechen. Wir können nicht lesen aus es.
Wir verwenden <? extends T> Objekte der Typen lesen von der Liste gleich oder unterhalb von T. Wir nicht Element hinzufügen können.

Bitte beachten Sie:

Verbraucher essen Abendessen (Super-); Produzent erweitert seine Eltern Fabrik

Kovarianz : akzeptieren Subtypen
Kontra : akzeptieren geordneten Typen

Covariant Typen sind schreibgeschützt, während kontra Typen sind schreib nur.

Mit richtigen Beispiel (mit einigen Vereinfachungen):

  1. Stellen Sie sich einen Güterzug mit Güterwagen als Analogie zu einer Liste an.
  2. Sie können put eine Ladung in einem Güterwagen, wenn die Ladung hat gleich oder kleiner Größe als die Güterwagen = <? super FreightCarSize>
  3. Sie können Entladen eine Ladung von einem Güterwagen, wenn Sie genug Platz (mehr als die Größe der Ladung) in Ihrem Depot = <? extends DepotSize>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top