อะไรคือความแตกต่างระหว่าง <? super E> และ <? ขยาย E> หรือไม่


147

ความแตกต่างระหว่าง<? super E>และ<? extends E>คืออะไร?

ตัวอย่างเช่นเมื่อคุณดูคลาสjava.util.concurrent.LinkedBlockingQueueมีลายเซ็นต่อไปนี้สำหรับตัวสร้าง:

public LinkedBlockingQueue(Collection<? extends E> c)

และหนึ่งสำหรับวิธีการ:

public int drainTo(Collection<? super E> c)

คำตอบ:


182

คนแรกบอกว่ามันเป็น "บางประเภทซึ่งเป็นบรรพบุรุษของ E"; ที่สองบอกว่ามันเป็น "บางประเภทซึ่งเป็น subclass ของ E" (ในทั้งสองกรณี E เองก็ไม่เป็นไร)

ตัวสร้างใช้? extends Eแบบฟอร์มดังนั้นจึงรับประกันได้ว่าเมื่อมันดึงค่าจากการรวบรวมพวกเขาทั้งหมดจะเป็น E หรือคลาสย่อยบางส่วน (นั่นคือมันเข้ากันได้) drainToวิธีการพยายามที่จะใส่ค่าลงไปในคอลเลกชันเพื่อให้คอลเลกชันจะต้องมีประเภทองค์ประกอบของE หรือ superclass

ตัวอย่างเช่นสมมติว่าคุณมีลำดับชั้นของชั้นเรียนเช่นนี้:

Parent extends Object
Child extends Parent

LinkedBlockingQueue<Parent>และ คุณสามารถสร้างการส่งผ่านนี้ในList<Child>ซึ่งจะคัดลอกองค์ประกอบทั้งหมดอย่างปลอดภัยเพราะทุกคนChildเป็นผู้ปกครอง คุณไม่สามารถส่งผ่านในList<Object>เนื่องจากองค์ประกอบบางอย่างอาจเข้ากันไม่ได้Parentเพราะองค์ประกอบบางอย่างอาจจะไม่สอดคล้องกับ

ในทำนองเดียวกันคุณสามารถระบายคิวว่าเป็นList<Object>เพราะทุกParentเป็นObject... แต่คุณไม่สามารถระบายลงList<Child>เพราะคาดว่าทุกองค์ประกอบของการเข้ากันได้กับList<Child>Child


25
+1 นั่นคือความแตกต่างในทางปฏิบัติจริง ๆ ขยายเพื่อดึง super เพื่อแทรก
Yishai

1
@ จอนคุณหมายความว่าอย่างไร (ในทั้งสองกรณีตัว E เองก็ไม่เป็นไร) ในย่อหน้าแรก?
Geek

2
@Geek: ฉันหมายความว่าถ้าคุณมีอะไรที่ชอบ? extends InputStreamหรือ? super InputStreamคุณสามารถใช้InputStreamเป็นอาร์กิวเมนต์
Jon Skeet

ฉันไม่เคยได้รับคำอธิบาย PECS จริงๆโดย Josh Block ใน Java ที่มีประสิทธิภาพ อย่างไรก็ตาม @Yishai นี่เป็นวิธีที่เป็นประโยชน์ในการจำ บางทีเราสามารถเสนอตัวช่วยจำใหม่ SAGE: Super -> เพิ่ม / รับ -> ขยาย
dcompiled

ดังนั้นถ้าฉันอ่านถูกต้อง "<? extends E>" ต้องการสิ่งนั้น "?" เป็นคลาสย่อยของ "E" และ "<? super E>" ต้องการให้ "E" เป็นคลาสย่อยของ "?" ใช่ไหม
El Suscriptor Justiciero

130

เหตุผลนี้ขึ้นอยู่กับวิธีการที่จาวาดำเนินการเรื่อง generics

ตัวอย่างอาร์เรย์

ด้วย Array คุณสามารถทำได้ (Array เป็น covariant)

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

แต่จะเกิดอะไรขึ้นถ้าคุณพยายามทำเช่นนี้?

myNumber[0] = 3.14; //attempt of heap pollution

บรรทัดสุดท้ายนี้จะรวบรวมได้ แต่ถ้าคุณเรียกใช้รหัสนี้คุณจะได้รับ ArrayStoreExceptionแต่ถ้าคุณเรียกใช้รหัสนี้คุณอาจได้รับ เพราะคุณกำลังพยายามใส่ double ลงในอาร์เรย์จำนวนเต็ม (โดยไม่คำนึงถึงการเข้าถึงผ่านการอ้างอิงตัวเลข)

ซึ่งหมายความว่าคุณสามารถหลอกคอมไพเลอร์ แต่คุณไม่สามารถหลอกระบบประเภทรันไทม์ และนี่ก็เป็นเช่นนั้นเพราะอาร์เรย์เป็นสิ่งที่เราเรียกว่าชนิดที่เรียกคืนได้ ซึ่งหมายความว่าที่รันไทม์ Java Number[]รู้ว่าอาร์เรย์นี้ที่อินสแตนซ์จริงเป็นอาร์เรย์ของจำนวนเต็มซึ่งก็เกิดขึ้นจะเข้าถึงได้ผ่านการอ้างอิงของพิมพ์

อย่างที่คุณเห็นสิ่งหนึ่งคือประเภทของวัตถุที่แท้จริงและอีกอย่างคือประเภทของการอ้างอิงที่คุณใช้เข้าถึงมันใช่ไหม

ปัญหาเกี่ยวกับ Java Generics

ตอนนี้ปัญหาเกี่ยวกับประเภททั่วไปของ Java คือข้อมูลประเภทถูกทิ้งโดยคอมไพเลอร์และไม่สามารถใช้ได้ในเวลาทำงาน กระบวนการนี้เรียกว่าลบออกประเภท มีเหตุผลที่ดีสำหรับการใช้งาน generics เช่นนี้ใน Java แต่นั่นเป็นเรื่องที่ยาวและต้องทำเหนือสิ่งอื่นใดด้วยความเข้ากันได้ของไบนารีกับรหัสที่มีอยู่ก่อน (ดูวิธีที่เราได้รับข้อมูลทั่วไปที่เรามี )

แต่จุดสำคัญที่นี่ก็คือตั้งแต่ที่รันไทม์ไม่มีข้อมูลประเภทไม่มีทางที่จะมั่นใจได้ว่าเราไม่ได้ก่อมลภาวะเป็นกอง

ตัวอย่างเช่น

List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);

List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution

หากคอมไพเลอร์ Java ไม่ได้หยุดคุณจากการทำเช่นนี้ระบบชนิดรันไทม์ไม่สามารถหยุดคุณได้เช่นกันเนื่องจากไม่มีวิธีรันไทม์เพื่อตรวจสอบว่ารายการนี้ควรจะเป็นรายการของจำนวนเต็มเท่านั้น รันไทม์ Java จะช่วยให้คุณใส่สิ่งที่คุณต้องการลงในรายการนี้เมื่อมันควรจะมีจำนวนเต็มเท่านั้นเพราะเมื่อมันถูกสร้างขึ้นมันถูกประกาศว่าเป็นรายการของจำนวนเต็ม

ดังนั้นผู้ออกแบบของ Java จึงแน่ใจว่าคุณไม่สามารถหลอกผู้แปลได้ หากคุณไม่สามารถหลอกคอมไพเลอร์ได้ (อย่างที่เราสามารถทำได้กับอาเรย์) คุณไม่สามารถหลอกระบบประเภทรันไทม์

ด้วยเหตุนี้เราจึงกล่าวว่าประเภททั่วไปไม่สามารถนำกลับมาใช้ซ้ำได้

เห็นได้ชัดว่าสิ่งนี้จะขัดขวางความแตกต่าง ลองพิจารณาตัวอย่างต่อไปนี้:

static long sum(Number[] numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

ตอนนี้คุณสามารถใช้มันได้เช่นนี้:

Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};

System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));

แต่ถ้าคุณพยายามใช้รหัสเดียวกันกับคอลเลกชันทั่วไปคุณจะไม่ประสบความสำเร็จ:

static long sum(List<Number> numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

คุณจะได้รับข้อผิดพลาดคอมไพเลอร์ถ้าคุณพยายาม ...

List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);

System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error

การแก้ปัญหาคือการเรียนรู้ที่จะใช้สองคุณสมบัติที่มีประสิทธิภาพของ Java generics ที่เรียกว่าความแปรปรวนร่วมและความแปรปรวน

แปรปรวน

ด้วยความแปรปรวนร่วมคุณสามารถอ่านรายการจากโครงสร้างได้ แต่คุณไม่สามารถเขียนอะไรลงไปได้ ทั้งหมดนี้คือการประกาศที่ถูกต้อง

List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();

และคุณสามารถอ่านได้จากmyNums:

Number n = myNums.get(0); 

เนื่องจากคุณสามารถมั่นใจได้ว่าสิ่งใดก็ตามที่มีอยู่ในรายการจริงมันสามารถถูกถ่ายทอดไปยังหมายเลข (หลังจากที่ทุกสิ่งที่ขยายหมายเลขเป็นหมายเลขใช่มั้ย)

อย่างไรก็ตามคุณไม่ได้รับอนุญาตให้นำสิ่งใดเข้าไปในโครงสร้าง covariant

myNumst.add(45L); //compiler error

สิ่งนี้จะไม่ได้รับอนุญาตเนื่องจาก Java ไม่สามารถรับประกันประเภทของวัตถุที่แท้จริงในโครงสร้างทั่วไป สามารถเป็นอะไรก็ได้ที่ขยาย Number แต่คอมไพเลอร์ไม่แน่ใจ ดังนั้นคุณสามารถอ่าน แต่ไม่เขียน

contravariance

ด้วยความแตกต่างคุณสามารถทำสิ่งที่ตรงกันข้าม คุณสามารถใส่สิ่งต่าง ๆ ลงในโครงสร้างทั่วไป แต่คุณไม่สามารถอ่านออกมาได้

List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");

List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);

ในกรณีนี้ธรรมชาติที่แท้จริงของวัตถุนั้นคือ List of Objects และจากการที่คุณสามารถนำ Numbers เข้ามาโดยทั่วไปเนื่องจากตัวเลขทั้งหมดมี Object เป็นบรรพบุรุษร่วมกัน ดังนั้นตัวเลขทั้งหมดจึงเป็นวัตถุดังนั้นจึงถูกต้อง

อย่างไรก็ตามคุณไม่สามารถอ่านอะไรได้อย่างปลอดภัยจากโครงสร้างที่ขัดแย้งกันนี้โดยสันนิษฐานว่าคุณจะได้รับหมายเลข

Number myNum = myNums.get(0); //compiler-error

อย่างที่คุณเห็นถ้าคอมไพเลอร์อนุญาตให้คุณเขียนบรรทัดนี้คุณจะได้ ClassCastException ขณะใช้งานจริง

รับ / ใส่หลักการ

ดังนั้นให้ใช้ความแปรปรวนร่วมเมื่อคุณตั้งใจจะเอาค่าทั่วไปออกจากโครงสร้างใช้ความแปรปรวนเมื่อคุณตั้งใจจะใส่ค่าทั่วไปลงในโครงสร้างและใช้ประเภทสามัญที่แน่นอนเมื่อคุณต้องการทำทั้งสองอย่าง

ตัวอย่างที่ดีที่สุดที่ฉันมีคือสิ่งต่อไปนี้ที่คัดลอกตัวเลขทุกชนิดจากรายการหนึ่งไปยังอีกรายการ มันจะได้รับไอเท็มจากแหล่งที่มาและจะวางไอเท็มในเป้าหมายเท่านั้น

public static void copy(List<? extends Number> source, List<? super Number> target) {
    for(Number number : source) {
        target(number);
    }
}

ขอขอบคุณพลังแห่งความแปรปรวนร่วมและความแปรปรวนนี้ใช้ได้กับกรณีเช่นนี้:

List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();

copy(myInts, myObjs);
copy(myDoubles, myObjs);

27
คำตอบนี้ควรชนไปด้านบน คำอธิบายที่ดี
Suresh Atta

1
@ edwindalorzo มีคำสะกดผิดเล็กน้อยที่คุณต้องการแก้ไขภายใต้ Contravariance คุณพูดว่าList<Object> myObjs = new List<Object();(ซึ่งไม่มีการปิด>สำหรับครั้งที่สองObject)

ตัวอย่างที่ยอดเยี่ยมง่ายและชัดเจนของแนวคิดที่ละเอียดอ่อนเหล่านี้!
db1234

สิ่งที่คุณสามารถเพิ่มเพื่อช่วยให้ผู้อื่นจดจำสิ่งต่าง ๆ super.methodNameเมื่อคุณต้องการที่จะเรียกวิธีการจากระดับซุปเปอร์คุณใช้ เมื่อใช้<? super E>งานจะหมายถึง "บางอย่างในsuperทิศทาง" ตรงข้ามกับบางสิ่งในextendsทิศทาง ตัวอย่าง: Objectอยู่ในsuperทิศทางของNumber(เนื่องจากเป็นคลาสที่สูงมาก) และIntegerอยู่ในextendsทิศทาง (เนื่องจากมันขยายNumber)
BrainStorm.exe

59

<? extends E>กำหนดEเป็นขอบเขตบน: "สิ่งนี้สามารถถูกโยนไปE"

<? super E>กำหนดEเป็นขอบเขตล่าง: " Eสามารถส่งไปยังสิ่งนี้ได้"


6
นี่เป็นหนึ่งในบทสรุปที่เรียบง่าย / ปฏิบัติที่ดีที่สุดของความแตกต่างที่ฉันเคยเห็น
JAB

1
เป็นเวลาหลายทศวรรษแล้ว (กับ OOP) ฉันได้ต่อสู้กับความคิดที่ว่า "บน" และ "ต่ำ" โดยสัญชาตญาณ aggravating! สำหรับฉันนั้นObjectเป็นระดับที่ต่ำกว่าโดยแท้ ๆ แม้ว่ามันจะเป็นซูเปอร์คลาสที่ดีที่สุด (และถูกวาดในแนวตั้งใน UML หรือต้นไม้ที่คล้ายกัน) ฉันไม่สามารถยกเลิกการทำสิ่งนี้ได้แม้ว่าจะพยายามอย่างหนัก

3
@ tgm1024 "superclass" และ "subclass" ต้องมีปัญหามากมาย
David Moles

@DavidMoles ทำไม เห็นได้ชัดว่าคุณไม่ได้ติดตามสิ่งที่ฉันพูดเลย "superclass" คล้ายกับ "superset"; แนวคิดของความเชี่ยวชาญคือการลดการบังคับใช้ภายใต้ความสัมพันธ์ IS-A Apple IS-A Fruit Fruit (superclass) เป็นเซ็ตเซ็ตรวมถึง Apple (คลาสย่อย) เป็นเซ็ตย่อย ความสัมพันธ์ทางวาจานั้นดี สิ่งที่ฉันกำลังพูดถึงคือความคิดที่ว่า "ส่วนบน" และ "ต่ำลง" มีการแมปภายในที่เป็น "superset" และ "ส่วนย่อย" ควรหลีกเลี่ยงส่วนบนและส่วนล่างตามเงื่อนไขใน OO

1
@ tgm1024 "Super-" มาจากละตินสุดยอด "เหนือ" และ "ย่อย" จากละตินย่อย "ใต้ใต้" กล่าวคือนิรุกติศาสตร์ซุปเปอร์ขึ้นและย่อยลง
David Moles

12

ฉันจะลองตอบคำถามนี้ แต่เพื่อให้ได้คำตอบที่ดีจริงๆคุณควรตรวจสอบหนังสือชวา Effective Java (2nd Edition) เขาอธิบายถึงผู้ช่วยจำ PECS ซึ่งย่อมาจาก "Producer Extends, Consumer Super"

แนวความคิดคือถ้าคุณใช้รหัสค่าทั่วไปจากวัตถุแล้วคุณควรใช้ขยาย แต่ถ้าคุณกำลังสร้างค่าใหม่สำหรับประเภททั่วไปคุณควรใช้ super

ตัวอย่างเช่น:

public void pushAll(Iterable<? extends E> src) {
  for (E e: src) 
    push(e);
}

และ

public void popAll(Collection<? super E> dst) {
  while (!isEmpty())
    dst.add(pop())
}

แต่จริงๆคุณควรตรวจสอบหนังสือเล่มนี้: http://java.sun.com/docs/books/effective/


12

<? super E> วิธี any object including E that is parent of E

<? extends E> วิธี any object including E that is child of E .


คำตอบที่สั้นและหวาน
chirag soni

7

คุณอาจต้องการ google สำหรับข้อกำหนดที่ ขัดแย้งกัน (<? super E> ) และความแปรปรวนร่วม ( <? extends E>) ฉันพบว่าสิ่งที่มีประโยชน์ที่สุดเมื่อทำความเข้าใจข้อมูลทั่วไปคือให้ฉันเข้าใจวิธีการของCollection.addAll:

public interface Collection<T> {
    public boolean addAll(Collection<? extends T> c);
}

เช่นเดียวกับที่คุณต้องการเพิ่มStringa List<Object>:

List<Object> lo = ...
lo.add("Hello")

คุณควรจะเพิ่ม List<String> (หรือคอลเลกชันใด ๆString) ด้วยaddAllวิธีการ:

List<String> ls = ...
lo.addAll(ls)

อย่างไรก็ตามคุณควรตระหนักว่า List<Object>และ a List<String>ไม่เทียบเท่าและไม่เป็นซับคลาสของอดีต สิ่งที่จำเป็นคือแนวคิดของพารามิเตอร์ประเภทcovariantคือ<? extends T>บิต

เมื่อคุณมีนี้มันง่ายที่จะคิดว่าสถานการณ์ที่คุณต้องการcontravarianceยัง (ตรวจสอบComparableอินเตอร์เฟซ)


4

ก่อนคำตอบ; โปรดชัดเจนว่า

  1. Generics รวบรวมเวลาเท่านั้นเพื่อให้แน่ใจว่า TYPE_SAFETY จะไม่สามารถใช้งานได้ในช่วง RUNTIME
  2. การอ้างอิงกับ Generics เท่านั้นที่จะบังคับให้เกิดความปลอดภัยประเภท หากการอ้างอิงไม่ได้รับการประกาศด้วยยาชื่อสามัญมันจะทำงานได้โดยไม่ต้องพิมพ์ความปลอดภัย

ตัวอย่าง:

List stringList = new ArrayList<String>();
stringList.add(new Integer(10)); // will be successful.

หวังว่าสิ่งนี้จะช่วยให้คุณเข้าใจสัญลักษณ์แทนได้ชัดเจนยิ่งขึ้น

//NOTE CE - Compilation Error
//      4 - For

class A {}

class B extends A {}

public class Test {

    public static void main(String args[]) {

        A aObj = new A();
        B bObj = new B();

        //We can add object of same type (A) or its subType is legal
        List<A> list_A = new ArrayList<A>();
        list_A.add(aObj);
        list_A.add(bObj); // A aObj = new B(); //Valid
        //list_A.add(new String()); Compilation error (CE);
        //can't add other type   A aObj != new String();


        //We can add object of same type (B) or its subType is legal
        List<B> list_B = new ArrayList<B>();
        //list_B.add(aObj); CE; can't add super type obj to subclass reference
        //Above is wrong similar like B bObj = new A(); which is wrong
        list_B.add(bObj);



        //Wild card (?) must only come for the reference (left side)
        //Both the below are wrong;   
        //List<? super A> wildCard_Wrongly_Used = new ArrayList<? super A>();
        //List<? extends A> wildCard_Wrongly_Used = new ArrayList<? extends A>();


        //Both <? extends A>; and <? super A> reference will accept = new ArrayList<A>
        List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<A>();
                        list_4__A_AND_SuperClass_A = new ArrayList<Object>();
                      //list_4_A_AND_SuperClass_A = new ArrayList<B>(); CE B is SubClass of A
                      //list_4_A_AND_SuperClass_A = new ArrayList<String>(); CE String is not super of A  
        List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<A>();
                          list_4__A_AND_SubClass_A = new ArrayList<B>();
                        //list_4__A_AND_SubClass_A = new ArrayList<Object>(); CE Object is SuperClass of A


        //CE; super reference, only accepts list of A or its super classes.
        //List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<String>(); 

        //CE; extends reference, only accepts list of A or its sub classes.
        //List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<Object>();

        //With super keyword we can use the same reference to add objects
        //Any sub class object can be assigned to super class reference (A)                  
        list_4__A_AND_SuperClass_A.add(aObj);
        list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B();
        //list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object(); 
        //list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type

        //We can't put anything into "? extends" structure. 
        //list_4__A_AND_SubClass_A.add(aObj); compilation error
        //list_4__A_AND_SubClass_A.add(bObj); compilation error
        //list_4__A_AND_SubClass_A.add("");   compilation error

        //The Reason is below        
        //List<Apple> apples = new ArrayList<Apple>();
        //List<? extends Fruit> fruits = apples;
        //fruits.add(new Strawberry()); THIS IS WORNG :)

        //Use the ? extends wildcard if you need to retrieve object from a data structure.
        //Use the ? super wildcard if you need to put objects in a data structure.
        //If you need to do both things, don't use any wildcard.


        //Another Solution
        //We need a strong reference(without wild card) to add objects 
        list_A = (ArrayList<A>) list_4__A_AND_SubClass_A;
        list_A.add(aObj);
        list_A.add(bObj);

        list_B = (List<B>) list_4__A_AND_SubClass_A;
        //list_B.add(aObj); compilation error
        list_B.add(bObj);

        private Map<Class<? extends Animal>, List<? extends Animal>> animalListMap;

        public void registerAnimal(Class<? extends Animal> animalClass, Animal animalObject) {

            if (animalListMap.containsKey(animalClass)) {
                //Append to the existing List
                 /*    The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject);
                 could return a List<Donkey>, List<Mouse>, List<Pikachu>, assuming Donkey, Mouse, and Pikachu were all sub classes of Animal. 
                 However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal.      
                 */   
                //List<? extends Animal> animalList = animalListMap.get(animalObject);
                //animalList.add(animalObject);  //Compilation Error because of List<? extends Animal>
                List<Animal> animalList = animalListMap.get(animalObject);
                animalList.add(animalObject);      


            } 
    }

    }
}


คำตอบของฉันสำหรับประเภทคลาสทั่วไปstackoverflow.com/questions/462297/how-to-use-classt-in-java/…
Kanagavelu Sugumar

1
คำอธิบายที่ดีพร้อมรหัส แต่ถ้าคุณใช้ความคิดเห็นในโค้ดด้านนอกบล็อครหัสมันจะดีกว่าสำหรับการดูและอ่านง่ายขึ้น
Prabhu

3

อักขระตัวแทนที่มีขอบเขตบนดูเหมือนว่า "? ขยายประเภท" และย่อมาจากตระกูลของทุกประเภทที่เป็นประเภทย่อยของประเภทประเภทที่จะรวม ประเภทเรียกว่าขอบเขตบน

อักขระตัวแทนที่มีขอบเขตล่างจะมีลักษณะเป็น "? super Type" และย่อมาจากตระกูลของทุกประเภทที่เป็นประเภท supertype ของ Type, Type Type ที่รวมอยู่ด้วย ประเภทนี้เรียกว่าขอบเขตล่าง


1

คุณมีคลาส Parent และคลาส Child ที่สืบทอดมาจากคลาส Parent คลาส Parent ถูกสืบทอดจากคลาสอื่นที่เรียกว่า GrandParent Class.So ลำดับการสืบทอดคือ GrandParent> Parent> Child ตอนนี้ <? ขยาย Parent> - สิ่งนี้ยอมรับคลาส Parent หรือคลาส Child <? super Parent> - สิ่งนี้ยอมรับคลาส Parent หรือคลาส GrandParent

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.