ฉันเจอ PECS (ย่อมาจากผู้ผลิตextends
และผู้บริโภคsuper
) ในขณะที่อ่านข้อมูลเกี่ยวกับยาชื่อสามัญ
มีใครอธิบายให้ฉันรู้วิธีใช้ PECS เพื่อแก้ไขความสับสนระหว่างextends
และsuper
?
ฉันเจอ PECS (ย่อมาจากผู้ผลิตextends
และผู้บริโภคsuper
) ในขณะที่อ่านข้อมูลเกี่ยวกับยาชื่อสามัญ
มีใครอธิบายให้ฉันรู้วิธีใช้ PECS เพื่อแก้ไขความสับสนระหว่างextends
และsuper
?
คำตอบ:
tl; dr: "PECS" มาจากมุมมองของคอลเล็กชัน หากคุณกำลังเพียงดึงรายการจากคอลเลกชันทั่วไปก็เป็นผู้ผลิตและคุณควรใช้extends
; ถ้าคุณกำลังเพียงsuper
บรรจุในรายการก็เป็นผู้บริโภคและคุณควรใช้ ถ้าคุณทำทั้งที่มีคอลเลกชันเดียวกันคุณไม่ควรใช้อย่างใดอย่างหนึ่งหรือextends
super
สมมติว่าคุณมีวิธีการที่จะใช้เวลาเป็นพารามิเตอร์ของคอลเลกชันของสิ่งที่ 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 ของใด ๆนอกจากนี้ยังมีT
T
เมื่อชนิดแปรถูกส่งผ่านไปยังวิธีการที่จะใช้กรณีของ
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
ส่วนหนึ่ง แต่ให้ความคิดกับคนอื่น