ฉันมีคำถามสองสามข้อเกี่ยวกับไวด์การ์ดทั่วไปใน Java:
อะไรคือความแตกต่างระหว่าง
List<? extends T>
และList<? super T>
?wildcard ที่มีขอบเขตคืออะไรและ wildcard ที่ไม่มีขอบเขตคืออะไร?
ฉันมีคำถามสองสามข้อเกี่ยวกับไวด์การ์ดทั่วไปใน Java:
อะไรคือความแตกต่างระหว่างList<? extends T>
และList<? super T>
?
wildcard ที่มีขอบเขตคืออะไรและ wildcard ที่ไม่มีขอบเขตคืออะไร?
คำตอบ:
ในคำถามแรกของคุณ<? extends T>
และ<? super T>
เป็นตัวอย่างของสัญลักษณ์แทนแบบมีขอบเขต มีลักษณะสัญลักษณ์แทนมากมายเช่นและโดยทั่วไปหมายถึง<?>
<? extends Object>
หมายความว่าทั่วไปอย่างหลวม ๆ อาจเป็นประเภทใดก็ได้ สัญลักษณ์แทนแบบมีขอบเขต ( <? extends T>
หรือ<? super T>
) กำหนดข้อ จำกัด เกี่ยวกับประเภทโดยบอกว่าต้องขยายประเภทเฉพาะ ( <? extends T>
เรียกว่าขอบเขตบน) หรือต้องเป็นบรรพบุรุษของประเภทที่เฉพาะเจาะจง ( <? super T>
เรียกว่าขอบเขตล่าง) .
ชวาสอนมีบางคำอธิบายที่ดีงามของยาชื่อสามัญในบทความตัวแทนและสนุกยิ่งขึ้นกับตัวแทน
<? super C>
หมายความว่าประเภทของคุณถูก จำกัด ไว้เฉพาะบางอย่างข้างต้นC
ในลำดับชั้นประเภท (ขออภัยสำหรับการตอบกลับช้ามากฉันเดาว่าเราไม่มีการแจ้งเตือนความคิดเห็นเมื่อ 2 ปีก่อน)
หากคุณมีลำดับชั้นของคลาส A, B คือคลาสย่อยของ A และ C และ D ทั้งสองเป็นคลาสย่อยของ B เช่นด้านล่าง
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
แล้ว
List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();
List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile
public void someMethod(List<? extends B> lb) {
B b = lb.get(0); // is fine
lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}
public void otherMethod(List<? super B> lb) {
B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
lb.add(new B()); // is fine, as we know that it will be a super type of A
}
สัญลักษณ์แทนขอบเขตก็เหมือนกับ? extends B
ที่ B เป็นบางประเภท นั่นคือไม่ทราบประเภท แต่สามารถวาง "ผูก" ไว้ได้ ในกรณีนี้มันถูกล้อมรอบด้วยคลาสบางคลาสซึ่งเป็นคลาสย่อยของ B
List<? super B>
อธิบายว่ารายการยอมรับประเภทที่เป็นคลาสแม่ของคลาส Bหรือไม่ ซึ่งเป็นสาเหตุที่ C และ D ไม่รวบรวมหืม?
Josh Bloch ยังมีคำอธิบายที่ดีว่าควรใช้เมื่อใดsuper
และextends
ในวิดีโอพูดคุยของ google ioนี้ซึ่งเขากล่าวถึงผู้ช่วยจำProducer extends
Consumersuper
จากสไลด์นำเสนอ:
สมมติว่าคุณต้องการเพิ่มวิธีการจำนวนมากให้
Stack<E>
void pushAll(Collection<? extends E> src);
- src เป็นผู้ผลิต E
void popAll(Collection<? super E> dst);
- dst เป็นผู้บริโภค E
อาจมีบางครั้งที่คุณต้องการ จำกัด ประเภทของประเภทที่อนุญาตให้ส่งผ่านไปยังพารามิเตอร์ type ตัวอย่างเช่นวิธีการที่ดำเนินการกับตัวเลขอาจต้องการยอมรับเฉพาะอินสแตนซ์ของ Number หรือคลาสย่อยเท่านั้น นี่คือพารามิเตอร์ชนิดที่มีขอบเขต
Collection<? extends MyObject>
หมายความว่าสามารถยอมรับวัตถุทั้งหมดที่มีความสัมพันธ์IS- Aกับ MyObject (เช่นวัตถุใด ๆ ที่เป็นประเภทของ myObject หรือเราสามารถพูดว่าวัตถุใด ๆ ของคลาสย่อยของ MyObject) หรือวัตถุของคลาส MyObject
ตัวอย่างเช่น:
class MyObject {}
class YourObject extends MyObject{}
class OurObject extends MyObject{}
จากนั้น
Collection<? extends MyObject> myObject;
จะยอมรับเฉพาะ MyObject หรือลูก ๆ ของ MyObject (เช่นออบเจ็กต์ประเภท OurObject หรือ YourObject หรือ MyObject ใด ๆ แต่ไม่ใช่วัตถุระดับสูงของ MyObject)
โดยทั่วไปแล้ว
หากโครงสร้างมีองค์ประกอบที่มีประเภทของรูปแบบ
? extends E
เราสามารถดึงองค์ประกอบออกจากโครงสร้างได้ แต่เราไม่สามารถใส่องค์ประกอบลงในโครงสร้างได้
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");
ที่จะนำองค์ประกอบในโครงสร้างที่เราต้องการชนิดของสัญลักษณ์แทนอื่นที่เรียกว่าWildcards with super
,
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
ไวลด์การ์ดทั่วไปถูกสร้างขึ้นเพื่อทำให้เมธอดที่ทำงานบนคอลเล็กชันสามารถใช้ซ้ำได้มากขึ้น
ตัวอย่างเช่นหากเมธอดมีพารามิเตอร์List<A>
เราสามารถให้เฉพาะList<A>
เมธอดนี้ มันเป็นการสูญเปล่าสำหรับการใช้วิธีนี้ในบางสถานการณ์:
List<A>
นั้นเราควรได้รับอนุญาตให้List<A-sub>
ใช้วิธีนี้ (เพราะ A-sub คือ A)List<A>
เท่านั้นเราควรได้รับอนุญาตให้List<A-super>
ใช้วิธีนี้ (เพราะ A คือ A-super)