905 Stimmen

Was ist PECS (Producer Extends Consumer Super)?

Ich stieß auf PECS (kurz für Produzent extends und Verbraucher super ), während ich mich über Generika informierte.

Kann mir jemand erklären, wie man PECS einsetzt, um die Verwirrung zwischen extends y super ?

6 Stimmen

Eine sehr gute Erklärung mit einem Beispiel @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630, das erklärt super Teil, sondern gibt eine Vorstellung von einem anderen.

1042voto

Michael Myers Punkte 183216

tl;dr: "PECS" ist aus der Sicht der Sammlung. Wenn Sie seulement Elemente aus einer generischen Sammlung abrufen, ist es ein Produzent und Sie sollten extends ; wenn Sie seulement Gegenstände hineinzustopfen, ist es ein Verbraucher und Sie sollten super . Wenn Sie beides mit der gleichen Sammlung tun, sollten Sie keine der beiden Methoden verwenden extends o super .


Nehmen wir an, Sie haben eine Methode, die als Parameter eine Sammlung von Dingen annimmt, aber Sie möchten, dass sie flexibler ist als nur die Annahme einer Collection<Thing> .

Fall 1: Sie wollen die Sammlung durchgehen und mit jedem Gegenstand etwas machen.
Dann ist die Liste eine Hersteller Sie sollten also eine Collection<? extends Thing> .

Die Argumentation lautet, dass ein Collection<? extends Thing> könnte jeden Subtyp von Thing und somit verhält sich jedes Element wie eine Thing wenn Sie Ihre Operation durchführen. (Sie können eigentlich nichts (außer null) zu einer Collection<? extends Thing> weil Sie zur Laufzeit nicht wissen können, welche spezifisch Untertyp von Thing die Sammlung enthält.)

Fall 2: Sie möchten der Sammlung etwas hinzufügen.
Dann ist die Liste eine Verbraucher Sie sollten also eine Collection<? super Thing> .

Die Argumentation lautet hier, dass im Gegensatz zu Collection<? extends Thing> , Collection<? super Thing> kann immer eine Thing unabhängig vom tatsächlichen parametrisierten Typ. Hier ist es egal, was bereits in der Liste steht, solange es eine Thing hinzugefügt werden; dies ist ? super Thing Garantien.

206 Stimmen

Ich versuche immer, auf diese Weise zu denken: A Hersteller ist es erlaubt, etwas Spezifischeres zu produzieren, daher erweitert , a Verbraucher etwas Allgemeineres annehmen darf, also super .

12 Stimmen

Eine andere Möglichkeit, sich an die Unterscheidung zwischen Produzent und Konsument zu erinnern, ist der Gedanke an eine Methodensignatur. Wenn Sie eine Methode haben doSomethingWithList(List list) sind Sie Verzehr von die Liste und benötigen daher eine Kovarianz/Erweiterung (oder eine invariante Liste). Andererseits, wenn Ihre Methode List doSomethingProvidingList dann sind Sie Herstellung von die Liste und benötigt Kontravarianz/Super (oder eine invariante Liste).

3 Stimmen

@MichaelMyers: Warum können wir nicht einfach einen parametrisierten Typ für diese beiden Fälle verwenden? Gibt es irgendeinen besonderen Vorteil, hier Platzhalter zu verwenden, oder ist es nur ein Mittel zur Verbesserung der Lesbarkeit, ähnlich wie bei der Verwendung von Referenzen auf const als Methodenparameter in C++, um zu zeigen, dass die Methode die Argumente nicht verändert?

679voto

anoopelias Punkte 8742

Die Prinzipien, die dahinter stehen, werden in der Informatik als

  • Kovarianz: ? extends MyClass ,
  • Kontravarianz: ? super MyClass y
  • Invarianz/Nicht-Varianz: MyClass

Das folgende Bild soll das Konzept erklären. Bild mit freundlicher Genehmigung: Andrej Tjukin

Covariance vs Contravariance

201 Stimmen

Hallo zusammen. Ich bin Andrey Tyukin, ich wollte nur bestätigen, dass anoopelias & DaoWen mich kontaktiert haben und meine Erlaubnis erhalten haben, die Skizze zu verwenden, sie ist unter (CC)-BY-SA lizenziert. Vielen Dank @ Anoop, dass du ihm ein zweites Leben geschenkt hast^^ @Brian Agnew: (zu "wenige Stimmen"): Das liegt daran, dass es eine Skizze für Scala ist, sie verwendet die Scala-Syntax und geht von einer Deklarations-Seiten-Varianz aus, die sich von der seltsamen Aufruf-Seiten-Varianz von Java unterscheidet... Vielleicht sollte ich eine detailliertere Antwort schreiben, die klar zeigt, wie diese Skizze auf Java anwendbar ist...

4 Stimmen

Dies ist eine der einfachsten und klarsten Erklärungen für Kovarianz und Kontravarianz, die ich je gefunden habe!

0 Stimmen

@Andrey Tyukin Hallo, ich möchte dieses Bild auch verwenden. Wie kann ich Sie kontaktieren?

94voto

Premraj Punkte 65511

Beim Umgang mit Sammlungen ist eine gängige Regel für die Auswahl zwischen nach oben oder unten begrenzten Platzhaltern PECS. Kredit

PECS (Produzent extends und Verbraucher super )

mnemotechnisch Ge t (ex t Ende) und P u t (S u per) Prinzip.

  • Este Prinzip heißt es:

    • Verwenden Sie eine extends Wildcard, wenn Sie nur Werte aus einer Struktur erhalten.
    • Verwenden Sie eine super Wildcard, wenn Sie nur Werte in eine Struktur eingeben.
    • Und verwenden Sie keinen Platzhalter, wenn Sie sowohl get als auch put verwenden.

Beispiel in Java:

class Super {
        Number testCoVariance() {
            return null;
        }
        void testContraVariance(Number parameter) {
        } 
    }

    class Sub extends Super {
        @Override
        Integer testCoVariance() {
            return null;
        } //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
        @Override
        void testContraVariance(Integer parameter) {
        } //doesn't support even though Integer is subtype of Number
    }

Das Liskov-Substitutionsprinzip (LSP) besagt, dass " Objekte in einem Programm sollten durch Instanzen ihrer Subtypen ersetzt werden können, ohne die Korrektheit des Programms zu verändern ".

Innerhalb des Typsystems einer Programmiersprache ist eine Typisierungsregel

  • Kovariante wenn es die Reihenfolge der Typen () beibehält, die die Typen von spezifischer zu allgemeiner ordnet;
  • kontravariant wenn sie diese Reihenfolge umkehrt;
  • invariant oder nonvariant, wenn keines von beiden zutrifft.

Kovarianz und Kontravarianz

  • Nur-Lese-Datentypen (Quellen) können sein Kovariante ;
  • schreibgeschützte Datentypen (Senken) können sein kontravariant .
  • Veränderliche Datentypen, die sowohl als Quellen als auch als Senken fungieren, sollten invariant .

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

  • Kovariante : Eine Katze[] ist ein Tier[];
  • kontravariant : Ein Tier[] ist eine Katze[];
  • invariant : Ein Tier[] ist keine Katze[] und eine Katze[] ist kein 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

enter image description here Bild src

begrenzt (d.h. auf etwas zusteuern) Platzhalter : Es gibt 3 verschiedene Arten von Wildcards:

  • In-Varianz/Nicht-Varianz: ? o ? extends Object - Unbegrenzt Joker. Er steht für die Familie aller Arten. Verwenden Sie, wenn Sie sowohl bekommen und setzen.
  • Ko-Varianz: ? extends T ( Herrschaft von T Nachkommen) - ein Platzhalter mit einer obere Schranke . T ist die obere -höchste Klasse in der Vererbungshierarchie. Verwenden Sie eine extends Platzhalter, wenn Sie nur Siehe Werte aus einer Struktur.
  • Kontra-Varianz: ? super T ( Herrschaft von T Vorfahre) - ein Platzhalter mit einem untere Schranke . T ist die unter -höchste Klasse in der Vererbungshierarchie. Verwenden Sie eine super Platzhalter, wenn Sie nur Setzen Sie Werte in eine Struktur.

Anmerkung: Platzhalter ? bedeutet Null oder einmalig stellt eine unbekannter Typ . Der Platzhalter kann als Typ eines Parameters verwendet werden, niemals als Typargument für einen generischen Methodenaufruf, eine generische Klasseninstanzerstellung (d.h. wenn ein Platzhalter verwendet wird, der an keiner anderen Stelle im Programm verwendet wird, wie z.B. T )

enter image description here

 import java.util.ArrayList;
import java.util.List;

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 {

    public static void main(String[] args) {
        //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
        List<? extends Shape> intList5 = new ArrayList<Shape>();
        List<? extends Shape> intList6 = new ArrayList<Cricle>();
        List<? extends Shape> intList7 = new ArrayList<Rectangle>();
        List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.

        //? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
        List<? super Shape> inList5 = new ArrayList<Shape>();
        List<? super Shape> inList6 = new ArrayList<Object>();
        List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.

        //-----------------------------------------------------------
        Circle circle = new Circle();
        Shape shape = circle; // OK. Circle IS-A Shape

        List<Circle> circles = new ArrayList<>();
        List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape

        List<? extends Circle> circles2 = new ArrayList<>();
        List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>

        //-----------------------------------------------------------
        Shape shape2 = new Shape();
        Circle circle2= (Circle) shape2; // OK. with type casting

        List<Shape> shapes3 = new ArrayList<>();
        List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of  List<Shape> even Circle is subetype of Shape

        List<? super Shape> shapes4 = new ArrayList<>();
        List<? super Circle> circles4 = shapes4; //OK.
    }

    /*
     * Example for an upper bound wildcard (Get values i.e Producer `extends`)
     *
     * */
    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape()); //ERROR
        list.add(new Circle()); // ERROR
        list.add(new Square()); // ERROR
        list.add(new Rectangle()); // ERROR
        Shape shape= list.get(0);//OK 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 knowthat 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 Object());//ERROR
        list.add(new Shape());//OK
        list.add(new Circle());//OK
        list.add(new Square());//OK
        list.add(new Rectangle());//OK
        Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
        Object object= list.get(0); //OK 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 y Beispiele

Kovarianz und Kontravarianz die Kompatibilität anhand der Typen zu bestimmen. In beiden Fällen ist die Varianz eine gerichtete Beziehung. Kovarianz kann übersetzt werden mit " unterschiedlich in dieselbe Richtung ," oder mit-unterschiedlich, während Kontravarianz bedeutet " anders in der entgegengesetzten Richtung ," oder gegen-verschieden. Kovariante und kontravariante Typen sind nicht dasselbe, aber es gibt eine Korrelation zwischen ihnen. Die Namen geben die Richtung der Korrelation an.

https://stackoverflow.com/a/54576828/1697099
https://stackoverflow.com/a/64888058/1697099

  • Kovarianz: akzeptiert Subtypen (nur lesen, d.h. Produzent)
  • Kontravarianz: Supertypen akzeptieren (nur schreiben, d. h. Verbraucher)

0 Stimmen

Hey, ich wollte nur wissen, was du mit dem letzten Satz gemeint hast: "Wenn du denkst, dass meine Analogie falsch ist, aktualisiere sie bitte". Meinst du, ob sie ethisch falsch ist (was subjektiv ist) oder ob sie im Kontext der Programmierung falsch ist (was objektiv ist: nein, sie ist nicht falsch)? Ich würde es gerne durch ein neutraleres Beispiel ersetzen, das unabhängig von kulturellen Normen und ethischen Überzeugungen universell akzeptabel ist.

0 Stimmen

Endlich konnte ich sie bekommen. Nette Erklärung.

2 Stimmen

@Premraj, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put. kann ich kein Element zu List<?> oder List<? extends Object> hinzufügen, also verstehe ich nicht, warum es möglich ist Use when you both get and put .

34voto

Gab Punkte 7400
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
    }
}

0 Stimmen

Daher sollte "? extends B" als "? B erweitert". Es ist etwas, das B erweitert, also würde das alle Superklassen von B bis zu Object einschließen, außer B selbst. Vielen Dank für den Code!

4 Stimmen

@SaurabhPatil Nein, ? extends B bedeutet B und alles, was B erweitert.

0 Stimmen

Welche Bedeutung haben die Zeilen mit der Aufschrift "nicht kompilierbar"? Nach meinem Verständnis können wir einer "extends"-Liste nichts anderes als null hinzufügen; wir können keine As, Bs oder Cs hinzufügen. CoVarience könnte durch das Scheitern der Kompilierung von C c = myBlist.get(0) demonstriert werden; In ähnlicher Weise scheitern in der Contrvarience-Methode alle Zuweisungen, außer der Zuweisung zu Object. Wenn es nicht gelingt, ein A hinzuzufügen, zeigt dies Contravarience.

32voto

Pradeep Kr Kaushal Punkte 1416

Kurz gesagt, drei einfache Regeln, um sich PECS zu merken:

  1. Verwenden Sie die <? extends T> Platzhalter, wenn Sie ein Objekt von Typs T aus einer Sammlung.
  2. Verwenden Sie die <? super T> Platzhalter, wenn Sie Objekte des Typs T in einer Sammlung.
  3. Wenn Sie beide Anforderungen erfüllen müssen, sollten Sie keinen Platzhalter verwenden. Als so einfach ist das.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X