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
?
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
?
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.
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 .
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).
@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?
Die Prinzipien, die dahinter stehen, werden in der Informatik als
? extends MyClass
,? super MyClass
yMyClass
Das folgende Bild soll das Konzept erklären. Bild mit freundlicher Genehmigung: Andrej Tjukin
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...
Dies ist eine der einfachsten und klarsten Erklärungen für Kovarianz und Kontravarianz, die ich je gefunden habe!
@Andrey Tyukin Hallo, ich möchte dieses Bild auch verwenden. Wie kann ich Sie kontaktieren?
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:
extends
Wildcard, wenn Sie nur Werte aus einer Struktur erhalten.super
Wildcard, wenn Sie nur Werte in eine Struktur eingeben.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
Um dieses allgemeine Phänomen zu veranschaulichen, betrachten wir den Typ Array. Für den Typ Tier können wir den Typ 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
begrenzt (d.h. auf etwas zusteuern) Platzhalter : Es gibt 3 verschiedene Arten von Wildcards:
?
o ? extends Object
- Unbegrenzt Joker. Er steht für die Familie aller Arten. Verwenden Sie, wenn Sie sowohl bekommen und setzen.? 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.? 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
)
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.
*/
}
}
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
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.
@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
.
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
}
}
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!
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.
Kurz gesagt, drei einfache Regeln, um sich PECS zu merken:
<? extends T>
Platzhalter, wenn Sie ein Objekt von Typs T
aus einer Sammlung.<? super T>
Platzhalter, wenn Sie Objekte des Typs T
in einer Sammlung. 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.
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.