ฉันเจอ PECS (ย่อมาจากผู้ผลิตextendsและผู้บริโภคsuper ) ในขณะที่อ่านข้อมูลเกี่ยวกับยาชื่อสามัญ
มีใครอธิบายให้ฉันรู้วิธีใช้ PECS เพื่อแก้ไขความสับสนระหว่างextendsและsuper?
ฉันเจอ PECS (ย่อมาจากผู้ผลิตextendsและผู้บริโภคsuper ) ในขณะที่อ่านข้อมูลเกี่ยวกับยาชื่อสามัญ
มีใครอธิบายให้ฉันรู้วิธีใช้ PECS เพื่อแก้ไขความสับสนระหว่างextendsและsuper?
คำตอบ:
tl; dr: "PECS" มาจากมุมมองของคอลเล็กชัน หากคุณกำลังเพียงดึงรายการจากคอลเลกชันทั่วไปก็เป็นผู้ผลิตและคุณควรใช้extends; ถ้าคุณกำลังเพียงsuperบรรจุในรายการก็เป็นผู้บริโภคและคุณควรใช้ ถ้าคุณทำทั้งที่มีคอลเลกชันเดียวกันคุณไม่ควรใช้อย่างใดอย่างหนึ่งหรือextendssuper
สมมติว่าคุณมีวิธีการที่จะใช้เวลาเป็นพารามิเตอร์ของคอลเลกชันของสิ่งที่ Collection<Thing>แต่คุณอยากให้มันเป็นความยืดหยุ่นมากขึ้นกว่าเพียงแค่การยอมรับ
กรณีที่ 1: คุณต้องการผ่านการรวบรวมและทำสิ่งต่าง ๆ กับแต่ละรายการ
แล้วรายการเป็นผู้ผลิตCollection<? extends Thing>ดังนั้นคุณจึงควรใช้
เหตุผลก็คือว่าCollection<? extends Thing>สามารถถือประเภทย่อยใด ๆThingและดังนั้นแต่ละองค์ประกอบจะทำหน้าที่เป็นThingเมื่อคุณดำเนินการของคุณ (จริง ๆ แล้วคุณไม่สามารถเพิ่มอะไรลงใน a Collection<? extends Thing>เนื่องจากคุณไม่สามารถรู้ได้ในขณะใช้งานจริงซึ่งมีประเภทย่อยเฉพาะของThingการเก็บสะสม)
กรณีที่ 2: คุณต้องการเพิ่มสิ่งต่าง ๆ ลงในคอลเลกชัน
แล้วรายการเป็นผู้บริโภคCollection<? super Thing>ดังนั้นคุณจึงควรใช้
เหตุผลที่นี่คือที่แตกต่างCollection<? extends Thing>กันCollection<? super Thing>สามารถถือThingไม่ว่าประเภทพารามิเตอร์ที่เกิดขึ้นจริงคืออะไร ที่นี่คุณไม่สนใจสิ่งที่มีอยู่แล้วในรายการตราบเท่าที่มันจะอนุญาตให้มีThingการเพิ่ม; นี่คือสิ่งที่? super Thingรับประกัน
doSomethingWithList(List list)คุณกำลังบริโภครายการและจะต้องมีความแปรปรวน / ขยาย (หรือรายการคงที่) ในทางตรงกันข้ามหากวิธีการของคุณคือList doSomethingProvidingListจากนั้นคุณจะผลิตรายการและจะต้อง contravariance / super (หรือรายการคงที่)
constพารามิเตอร์เมธอดใน C ++ เพื่อแสดงว่าวิธีนั้นไม่ได้แก้ไขข้อโต้แย้ง?
หลักการที่อยู่เบื้องหลังเรื่องนี้ในวิทยาการคอมพิวเตอร์เรียกว่า
? extends MyClass,? super MyClassและMyClassรูปภาพด้านล่างควรอธิบายแนวคิด รูปภาพมารยาท: Andrey Tyukin

PECS (ผู้ผลิตextendsและผู้บริโภคsuper)
mnemonic →รับและใส่หลักการ
หลักการนี้ระบุว่า:
ตัวอย่างใน 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: ถ้า S เป็นประเภทย่อยของ T ดังนั้นวัตถุประเภท T อาจถูกแทนที่ด้วยวัตถุประเภท S
ภายในระบบการพิมพ์ของภาษาการเขียนโปรแกรมกฎการพิมพ์
เพื่อแสดงปรากฏการณ์ทั่วไปนี้ให้พิจารณาประเภทของอาร์เรย์ สำหรับประเภทสัตว์เราสามารถสร้างประเภทสัตว์ []
ตัวอย่าง Java:
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
bounded (เช่นมุ่งหน้าไปยังที่ใดที่หนึ่ง) wildcard : มี 3 Wildcards ที่แตกต่างกัน:
?หรือ? extends Object- ไวด์การ์ดที่ไม่ได้ จำกัด มันหมายถึงครอบครัวทุกประเภท ใช้เมื่อคุณทั้งคู่ได้รับและวาง? extends T(ครอบครัวทุกประเภทที่มีชนิดย่อยของ T) - สัญลักษณ์แทนกับขอบเขตบน Tเป็นชั้นบนสุดในลำดับชั้นการสืบทอด ใช้extendsสัญลักษณ์แทนเมื่อคุณจะได้รับค่าจากโครงสร้าง? super T(ครอบครัวทุกประเภทที่มี supertypes ของ T) - สัญลักษณ์แทนด้วยเป็นขอบเขตล่าง Tเป็นที่ต่ำกว่าระดับ -most ในลำดับชั้นมรดก ใช้superสัญลักษณ์แทนเมื่อคุณใส่ค่าลงในโครงสร้างหมายเหตุ: สัญลักษณ์ตัวแทน?หมายถึงศูนย์หรือหนึ่งครั้งแสดงประเภทที่ไม่รู้จัก wildcard สามารถใช้เป็นชนิดของพารามิเตอร์, ไม่เคยใช้เป็นอาร์กิวเมนต์ชนิดสำหรับการเรียกใช้เมธอดทั่วไป, การสร้างอินสแตนซ์คลาสทั่วไป (เช่นเมื่อใช้ไวด์การ์ดที่อ้างอิงไม่ได้ใช้ในที่อื่น ๆ ในโปรแกรมเหมือนที่เราใช้T)
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.
*/
}
}
In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.ฉันไม่สามารถเพิ่มองค์ประกอบลงในรายการ <?> หรือรายการ <? ขยาย Object> ดังนั้นฉันไม่เข้าใจว่าทำไมจึงเป็นUse when you both get and putเช่นนั้น
?- "wildcard ที่ไม่ได้ จำกัด " - สอดคล้องกับสิ่งที่ตรงกันข้ามแน่นอน โปรดดูเอกสารประกอบต่อไปนี้: docs.oracle.com/javase/tutorial/java/generics/ ......ซึ่งระบุ: ในกรณีที่รหัสต้องการเข้าถึงตัวแปรเป็นทั้งตัวแปร "in" และ "out" ให้ทำ ไม่ใช้สัญลักษณ์แทน (พวกเขากำลังใช้ "ใน" และ "ออก" เป็นคำพ้องกับ "รับ" และ "ใส่") มีข้อยกเว้นของคุณไม่สามารถเพิ่มคอลเลกชันแปรด้วยnull ?
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
}
}
? extends Bหมายถึง B และทุกอย่างที่ขยาย B
ขณะที่ผมอธิบายในคำตอบของฉันคำถามอื่นเต้าเป็นอุปกรณ์ช่วยในการจำที่สร้างขึ้นโดยจอชโบลชช่วยจำP roducer extends, Csuper onsumer
ซึ่งหมายความว่าเมื่อมีการแปรชนิดจะถูกส่งผ่านไปยังวิธีการจะผลิตกรณีของ
T(พวกเขาจะถูกดึงจากมันในทางใดทางหนึ่ง)? extends Tควรจะใช้ตั้งแต่ตัวอย่างของ subclass ของใด ๆนอกจากนี้ยังมีTTเมื่อชนิดแปรถูกส่งผ่านไปยังวิธีการที่จะใช้กรณีของ
T(พวกเขาจะถูกส่งผ่านไปเพื่อทำอะไรบางอย่าง)? super Tควรจะนำมาใช้เพราะเป็นตัวอย่างของTถูกต้องตามกฎหมายได้รับการส่งผ่านไปยังวิธีการใด ๆ ที่ยอมรับ supertypeTบางComparator<Number>สามารถนำมาใช้ในCollection<Integer>ตัวอย่างเช่น? extends Tจะไม่ทำงานเพราะไม่สามารถทำงานบนComparator<Integer>Collection<Number>
โปรดทราบว่าโดยทั่วไปคุณควรใช้? extends Tและ? super Tพารามิเตอร์ของวิธีการบางอย่างเท่านั้น เมธอดควรใช้Tเป็นพารามิเตอร์ type บนประเภท return ทั่วไป
สรุปกฎง่ายๆสามข้อในการจดจำ PECS:
<? extends T>wildcard หากคุณต้องการดึงอ็อบเจกต์ประเภทTจากคอลเลกชัน<? super T>สัญลักษณ์แทนหากคุณต้องการใส่วัตถุประเภทTในคอลเลกชันสมมติว่าลำดับชั้นนี้:
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
ขอชี้แจง PE - ผู้ผลิตขยาย:
List<? extends Shark> sharks = new ArrayList<>();
ทำไมคุณไม่สามารถเพิ่มวัตถุที่ขยาย "ฉลาม" ในรายการนี้ ชอบ:
sharks.add(new HammerShark());//will result in compilation error
เนื่องจากคุณมีรายการที่สามารถเป็นประเภท A, B หรือ C ที่รันไทม์คุณจึงไม่สามารถเพิ่มวัตถุประเภท A, B หรือ C ในรายการเนื่องจากคุณสามารถท้ายด้วยชุดค่าผสมที่ไม่ได้รับอนุญาตใน java
ในทางปฏิบัติผู้แปลสามารถเห็น compiletime ที่คุณเพิ่ม B:
sharks.add(new HammerShark());
... แต่มันไม่มีวิธีที่จะบอกได้ว่าตอนรันไทม์ B ของคุณจะเป็นประเภทย่อยหรือ supertype ของชนิดรายการ ที่รันไทม์ชนิดรายการสามารถเป็นประเภท A, B, C ดังนั้นคุณจึงไม่สามารถเพิ่ม HammerSkark (super type) ในรายการ DeadHammerShark ได้
* คุณจะพูดว่า: "ตกลง แต่ทำไมฉันไม่สามารถเพิ่ม HammerSkark ในประเภทนี้เพราะมันเป็นประเภทที่เล็กที่สุด" คำตอบ: มันเล็กที่สุดที่คุณรู้ แต่ HammerSkark สามารถขยายได้โดยบุคคลอื่นเช่นกันและคุณจะจบลงในสถานการณ์เดียวกัน
ขอชี้แจง CS - Consumer Super:
ในลำดับชั้นเดียวกันเราสามารถลอง:
List<? super Shark> sharks = new ArrayList<>();
คุณสามารถเพิ่มอะไรลงในรายการนี้และทำไม
sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());
คุณสามารถเพิ่มประเภทของวัตถุด้านบนได้เนื่องจากสิ่งใดก็ตามที่อยู่ด้านล่างฉลาม (A, B, C) จะเป็นประเภทย่อยของสิ่งที่อยู่เหนือฉลาม (X, Y, Z) เสมอ เข้าใจง่าย
คุณไม่สามารถเพิ่มชนิดด้านบน Shark ได้เนื่องจากในขณะใช้งานจริงชนิดของวัตถุที่เพิ่มอาจมีลำดับชั้นได้สูงกว่าชนิดที่ประกาศของรายการ (X, Y, Z) สิ่งนี้ไม่ได้รับอนุญาต
แต่ทำไมคุณไม่สามารถอ่านจากรายการนี้ (ฉันหมายความว่าคุณสามารถเอาองค์ประกอบออกมาได้ แต่คุณไม่สามารถกำหนดให้กับสิ่งอื่นนอกเหนือจาก Object o):
Object o;
o = sharks.get(2);// only assignment that works
Animal s;
s = sharks.get(2);//doen't work
ที่รันไทม์ชนิดของรายการสามารถเป็นประเภทใดก็ได้เหนือ A: X, Y, Z, ... คอมไพเลอร์สามารถรวบรวมคำสั่งการมอบหมายของคุณ (ซึ่งดูเหมือนว่าถูกต้อง) แต่ที่รันไทม์ชนิดของ s (สัตว์) อาจต่ำกว่า ลำดับชั้นกว่าประเภทที่ประกาศของรายการ (ซึ่งอาจเป็นสิ่งมีชีวิตหรือสูงกว่า) สิ่งนี้ไม่ได้รับอนุญาต
เพื่อสรุป
เราใช้<? super T>เพื่อเพิ่มวัตถุประเภทเท่ากับหรือต่ำไปT Listเราอ่านไม่ออก
เราใช้<? extends T>เพื่ออ่านวัตถุประเภทเท่ากันหรือด้านล่างTจากรายการ เราไม่สามารถเพิ่มองค์ประกอบเข้าไปได้
(การเพิ่มคำตอบเพราะมีตัวอย่างไม่มากพอที่จะใช้ wildcard แทน)
// 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);
}
}
นี่เป็นวิธีที่ชัดเจนและง่ายที่สุดสำหรับฉันที่คิดว่ายืดออกและสุดยอด:
extendsสำหรับการอ่าน
superสำหรับการเขียน
ฉันพบว่า "PECS" เป็นวิธีที่ไม่ชัดเจนในการคิดถึงสิ่งต่าง ๆ เกี่ยวกับผู้ที่เป็น "ผู้ผลิต" และใครคือ "ผู้บริโภค" "PECS" ถูกกำหนดจากมุมมองของการรวบรวมข้อมูลตัวเอง - การรวบรวม "สิ้นเปลือง" หากวัตถุกำลังถูกเขียนลงไป (มันเป็นการสิ้นเปลืองวัตถุจากการเรียกรหัส) และ "สร้าง" ถ้าวัตถุกำลังอ่านจากมัน (มัน กำลังผลิตวัตถุเพื่อเรียกรหัสบางอย่าง) นี่เป็นสิ่งที่ตรงกันข้ามกับการตั้งชื่อทุกอย่างอื่น Java API มาตรฐานถูกตั้งชื่อจากมุมมองของรหัสการโทรไม่ใช่ตัวรวบรวมเอง ตัวอย่างเช่นมุมมองคอลเลกชันเป็นศูนย์กลางของjava.util.Listควรมีวิธีชื่อ "receive ()" แทน "add ()" - หลังจากทั้งหมดองค์ประกอบ แต่รายการตัวเองได้รับองค์ประกอบ
ฉันคิดว่ามันใช้งานง่ายกว่าเป็นธรรมชาติและสอดคล้องกับการคิดสิ่งต่าง ๆ จากมุมมองของรหัสที่โต้ตอบกับคอลเลกชัน - รหัส "อ่านจาก" หรือ "เขียนถึง" คอลเลกชันหรือไม่ ต่อจากนั้นการเขียนโค้ดใด ๆไปยังคอลเลกชันจะเป็น "ผู้สร้าง" และการอ่านโค้ดใด ๆจากคอลเลกชันจะเป็น "ผู้บริโภค"
src dstดังนั้นคุณกำลังจัดการกับทั้งรหัสและคอนเทนเนอร์ในเวลาเดียวกันและฉันก็คิดเกี่ยวกับมันตามบรรทัดเหล่านั้น - "การใช้รหัส" บริโภคจากภาชนะที่ผลิตและ "ผลิตรหัส" ผลิตสำหรับภาชนะที่บริโภค
"กฎ" PECS เพียงทำให้แน่ใจว่าสิ่งต่อไปนี้ถูกกฎหมาย:
?ก็สามารถอ้างถึงได้อย่างถูกกฎหมาย T ?จะเป็นอะไรก็สามารถอ้างถึงได้โดยชอบด้วยกฎหมาย Tการจับคู่ตามปกติList<? extends T> producer, List<? super T> consumerจะช่วยให้มั่นใจได้ว่าคอมไพเลอร์สามารถบังคับใช้กฎความสัมพันธ์การสืบทอดมาตรฐาน "IS-A" ถ้าเราสามารถทำเช่นนั้นถูกต้องตามกฎหมายก็อาจจะง่ายที่จะพูด<T extends ?>, <? extends T>(หรือดีกว่ายังอยู่ในสกาล่าที่คุณสามารถดูด้านบนมัน[-T], [+T]. <? super T>, <? extends T>แต่น่าเสียดายที่ดีที่สุดที่เราสามารถทำได้คือ
เมื่อฉันพบสิ่งนี้เป็นครั้งแรกและทำลายมันลงในหัวของฉันกลไกทำให้รู้สึก แต่รหัสตัวเองยังคงดูสับสนกับฉัน - ฉันยังคงคิดว่า "มันดูเหมือนว่าขอบเขตไม่ควรจะต้องคว่ำลงเช่นนั้น" - แม้ว่าฉัน มีความชัดเจนเกี่ยวกับข้างต้น - เป็นเพียงการรับประกันว่าจะปฏิบัติตามกฎการอ้างอิงมาตรฐาน
สิ่งที่ช่วยให้ฉันดูคือการใช้การมอบหมายธรรมดาเป็นอุปมา
พิจารณารหัสของเล่น (ยังไม่พร้อมผลิต) ต่อไปนี้:
// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
for(T t : producer)
consumer.add(t);
}
แสดงนี้ในแง่ของการเปรียบเทียบที่ได้รับมอบหมายสำหรับตัวแทน (ไม่ทราบชนิด) จะอ้างอิง - ที่ "ด้านซ้ายมือ" ของงาน - และเพื่อให้แน่ใจว่าสิ่งที่เป็น"IS-A" - ที่สามารถกำหนดให้กับมันเพราะเป็นชนิดสุด (หรืออย่างมากที่สุดชนิดเดียวกัน) เช่นconsumer?<? super T>?T?T?T
สำหรับproducerความกังวลเหมือนกันมันคว่ำเพียง: producer's ?ตัวแทน (ไม่ทราบชนิด) เป็นอ้างอิง - ที่ 'ด้านขวามือ' ของงาน - และ<? extends T>เพื่อให้แน่ใจว่าสิ่งที่?เป็น?'IS-A' T- ว่ามันจะได้รับมอบหมายให้เป็นTเพราะ?เป็นชนิดย่อย (หรืออย่างน้อยประเภทเดียวกัน) Tเช่น
จำสิ่งนี้ไว้:
ผู้บริโภคกินอาหารมื้อเย็น (super); ผู้ผลิตขยายโรงงานของพ่อแม่
ใช้ตัวอย่างชีวิตจริง (มีการทำให้เข้าใจง่าย):
<? super FreightCarSize><? extends DepotSize>แปรปรวน : ยอมรับเชื้อ
contravariance : ยอมรับ supertypes
ประเภท Covariant เป็นแบบอ่านอย่างเดียวในขณะที่ประเภทที่แปรปรวนเป็นแบบเขียนอย่างเดียว
ลองมาดูตัวอย่าง
public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }
ยาสามัญช่วยให้คุณสามารถทำงานกับประเภทแบบไดนามิกในวิธีที่ปลอดภัย
//ListA
List<A> listA = new ArrayList<A>();
//add
listA.add(new A());
listA.add(new B());
listA.add(new C());
//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();
//add
listB.add(new B());
//get
B b0 = listB.get(0);

เนื่องจากคอลเล็กชันของ Java เป็นประเภทอ้างอิงดังนั้นเราจึงมีปัญหาต่อไป:
ปัญหา # 1
//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* ทั่วไปของ Swift ไม่มีปัญหาดังกล่าวเนื่องจากการสะสมคือValue type[เกี่ยวกับ]ดังนั้นจึงมีการสร้างคอลเลกชันใหม่
ปัญหา # 2
//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

อักขระตัวแทนเป็นคุณลักษณะประเภทการอ้างอิงและไม่สามารถสร้างอินสแตนซ์ได้โดยตรง
โซลูชัน # 1
<? super A>หรือที่รู้จักกันในชื่อขอบเขตล่างหรือที่รู้จักกันว่าผู้บริโภครับรองว่าทำงานโดย A และซูเปอร์คลาสทั้งหมดนั่นคือเหตุผลว่าทำไมจึงปลอดภัยที่จะเพิ่ม
List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();
//add
listSuperA.add(new A());
listSuperA.add(new B());
//get
Object o0 = listSuperA.get(0);

โซลูชัน # 2
<? extends A>aka ความแปรปรวนร่วมบนอาคาผู้ผลิต aka รับประกันความถูกต้องว่าดำเนินการโดย A และคลาสย่อยทั้งหมดนั่นคือเหตุผลว่าทำไมจึงปลอดภัยที่จะรับและส่ง
List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;
//get
A a0 = listExtendsA.get(0);

superส่วนหนึ่ง แต่ให้ความคิดกับคนอื่น