PECS (निर्माता का विस्तार उपभोक्ता सुपर) क्या है?


729

मैं जेनरिक पर पढ़ते हुए PECS ( प्रोड्यूसर extendsऔर कंज्यूमर केsuper लिए कम ) के पार आया ।

क्या कोई मुझे समझा सकता है कि बीच भ्रम को हल करने के लिए PECS का उपयोग कैसे करें extendsऔर super?


3
एक उदाहरण @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 के साथ एक बहुत अच्छी व्याख्या जो superहिस्सा बताती है , लेकिन दूसरे का एक विचार देती है।
लुपचियाज़ोएम

जवाबों:


842

tl; dr: "PECS" संग्रह के दृष्टिकोण से है। यदि आप केवल सामान्य संग्रह से आइटम खींच रहे हैं , तो यह एक निर्माता है और आपको इसका उपयोग करना चाहिए extends; यदि आप केवल सामान भर रहे हैं, तो यह एक उपभोक्ता है और आपको इसका उपयोग करना चाहिए super। यदि आप दोनों एक ही संग्रह के साथ करते हैं, तो आपको extendsया तो उपयोग नहीं करना चाहिए या super


माना कि आपके पास एक ऐसा तरीका है जो इसके पैरामीटर को चीजों का एक संग्रह के रूप में लेता है, लेकिन आप इसे केवल स्वीकार करने की तुलना में अधिक लचीला होना चाहते हैं Collection<Thing>

केस 1: आप संग्रह के माध्यम से जाना चाहते हैं और प्रत्येक आइटम के साथ चीजें करना चाहते हैं।
फिर सूची एक निर्माता है , इसलिए आपको एक का उपयोग करना चाहिए Collection<? extends Thing>

तर्क यह है कि कोई Collection<? extends Thing>भी उप-प्रकार पकड़ सकता है Thing, और Thingजब आप अपना ऑपरेशन करते हैं तो प्रत्येक तत्व व्यवहार करेगा । (आप वास्तव में एक के लिए कुछ भी नहीं जोड़ सकते हैं Collection<? extends Thing>, क्योंकि आप क्रम जिस पर पता नहीं कर सकते हैं विशिष्ट की उप-प्रकार Thingसंग्रह आयोजित करता है।)

केस 2: आप चीजों को संग्रह में जोड़ना चाहते हैं।
फिर सूची एक उपभोक्ता है , इसलिए आपको एक का उपयोग करना चाहिए Collection<? super Thing>

यहां तर्क यह है कि इसके विपरीत Collection<? extends Thing>, Collection<? super Thing>हमेशा Thingकोई फर्क नहीं पड़ता कि वास्तविक पैरामीटर प्रकार क्या है। यहाँ आप परवाह नहीं करते कि सूची में पहले से ही क्या है जब तक कि यह एक Thingको जोड़ने की अनुमति देगा ; यह ? super Thingगारंटी देता है।


142
मैं हमेशा इसके बारे में इस तरह से सोचने की कोशिश कर रहा हूं: एक निर्माता को कुछ अधिक विशिष्ट उत्पादन करने की अनुमति दी जाती है, इसलिए विस्तारित होती है , एक उपभोक्ता को कुछ और सामान्य स्वीकार करने की अनुमति होती है, इसलिए सुपर
फुएरमुरल

10
निर्माता / उपभोक्ता के अंतर को याद रखने का एक और तरीका एक विधि हस्ताक्षर के बारे में सोचना है। यदि आपके पास एक विधि है doSomethingWithList(List list), तो आप सूची का उपभोग कर रहे हैं और इसलिए आपको सहसंयोजक / फैली (या एक अदृश्य सूची) की आवश्यकता होगी। दूसरी ओर यदि आपकी विधि है List doSomethingProvidingList, तो आप सूची का निर्माण कर रहे हैं और आपको कंट्रोवर्सी / सुपर (या एक अनियंत्रित सूची) की आवश्यकता होगी।
रमन

3
@MichaelMyers: हम इन दोनों मामलों के लिए केवल एक पैरामीटर प्रकार का उपयोग क्यों नहीं कर सकते? क्या यहां वाइल्डकार्ड्स का उपयोग करने का कोई विशिष्ट लाभ है, या यह सिर्फ constC ++ में विधि मापदंडों के संदर्भ के रूप में पठनीयता में सुधार करने का एक साधन है, यह दर्शाता है कि विधि तर्कों को संशोधित नहीं करती है?
चटर्जी

7
@ रामन, मुझे लगता है कि आपने इसे भ्रमित किया है। DoSthWithList (आपके पास सूची <? सुपर थिंग>) हो सकती है, चूंकि आप उपभोक्ता हैं, आप सुपर का उपयोग कर सकते हैं (याद रखें, सीएस)। हालाँकि, यह सूची है <? थिंग> getList () का विस्तार करता है क्योंकि आपको उत्पादन (PE) करते समय कुछ अधिक विशिष्ट वापस करने की अनुमति दी जाती है।
मास्टरएक्सिलो

4
@AZ_ मैं आपकी भावना साझा करता हूं। यदि सूची से कोई विधि मिलती है (), तो विधि को उपभोक्ता <T> माना जाएगा, और सूची को प्रदाता माना जाएगा; लेकिन PECS का नियम "सूची के दृष्टिकोण से" है, इस प्रकार 'विस्तार' के लिए कहा जाता है। यह GEPS होना चाहिए: विस्तार करना; सुपर लगाओ।
ट्रीफ़िश झांग

561

कंप्यूटर विज्ञान में इसके पीछे सिद्धांतों को कहा जाता है

  • सहप्रसरण: ? extends MyClass,
  • विपरीत: ? super MyClassऔर
  • निश्चरता / गैर-विचरण: MyClass

नीचे दी गई तस्वीर अवधारणा को समझाना चाहिए। चित्र सौजन्य: एंड्री टायरिन

कोवरियनस बनाम कंट्रावेरियन


143
ज़रा सुनिए सभी। मैं एंड्री टायुकिन हूं, मैं बस इस बात की पुष्टि करना चाहता था कि अनूपेलियास और दावेन ने मुझसे संपर्क किया और स्केच का उपयोग करने की मेरी अनुमति प्राप्त की, यह (CC) -BY-SA के तहत लाइसेंस प्राप्त है। Thx @ अनूप इसे दूसरा जीवन देने के लिए ^ ^ @Brian Agnew: ("कुछ वोटों पर"): ऐसा इसलिए है क्योंकि यह स्कैला के लिए एक स्केच है, यह स्काला सिंटैक्स का उपयोग करता है और घोषणा-साइट विचरण मानता है, जो जावा के अजीब कॉल के लिए काफी अलग है। -साइट विचरण ... शायद मुझे अधिक विस्तृत उत्तर लिखना चाहिए जो स्पष्ट रूप से दिखाता है कि यह स्केच जावा पर कैसे लागू होता है ...
एंड्री टायरिन

3
यह कोवरियनस और कॉन्ट्रैवेरियन के लिए सबसे सरल और स्पष्ट विवरणों में से एक है जो मैंने कभी पाया है!
सीएस

@Andrey Tyukin हाय, मैं भी इस छवि का उपयोग करना चाहता हूं। मै आपसे कैसे सम्पर्क कर सकता हूं?
slouc

यदि इस चित्रण के बारे में आपके कोई प्रश्न हैं, तो हम उनसे चैट रूम में चर्चा कर सकते हैं: chat.stackoverflow.com/rooms/145734/…
एंड्री टाइकिन


48

PECS (निर्माता extendsऔर उपभोक्ता super)

mnemonic → जाओ और रखो सिद्धांत।

यह सिद्धांत बताता है कि:

  • फैली हुई वाइल्डकार्ड का उपयोग करें जब आप केवल एक संरचना से मान प्राप्त करते हैं।
  • एक सुपर वाइल्डकार्ड का उपयोग करें जब आप केवल एक संरचना में मान डालते हैं।
  • और जब आप दोनों मिलते हैं तो एक वाइल्डकार्ड का उपयोग न करें।

जावा में उदाहरण:

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 टाइप की वस्तुओं से बदला जा सकता है।

एक प्रोग्रामिंग भाषा के प्रकार प्रणाली के भीतर, एक टाइपिंग नियम

  • सहसंयोजक अगर यह प्रकारों (which) के आदेश को संरक्षित करता है, जो अधिक विशिष्ट से अधिक सामान्य प्रकार के आदेश देता है;
  • यदि यह इस आदेश को उलट देता है, तो contravariant ;
  • यदि इनमें से कोई भी लागू नहीं होता है, तो अपरिवर्तनीय या गैर-भिन्न।

सहसंयोजक और विरोधाभासी

  • केवल-डेटा प्रकार (स्रोत) सहसंयोजक हो सकते हैं ;
  • राइट-ओनली डेटा टाइप्स (सिंक) कंट्राविरेंट हो सकते हैं ।
  • म्यूटेबल डेटा प्रकार जो दोनों स्रोतों और सिंक के रूप में कार्य करते हैं, उन्हें अपरिवर्तनीय होना चाहिए ।

इस सामान्य घटना को समझने के लिए, सरणी प्रकार पर विचार करें। पशु के प्रकार के लिए हम पशु बना सकते हैं []

  • सहसंयोजक : एक बिल्ली [] एक पशु है [];
  • contravariant : एक जानवर [] एक बिल्ली है [];
  • अपरिवर्तनीय : एक पशु [] एक बिल्ली नहीं है [] और एक बिल्ली [] एक पशु नहीं है []।

जावा उदाहरण:

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  

और ज्यादा उदाहरण

बाउंडेड (यानी कहीं की ओर बढ़ते हुए ) वाइल्डकार्ड : वाइल्डकार्ड के 3 अलग-अलग स्वाद हैं:

  • इन-विचरण / गैर-विचरण: ?या ? extends Object- अनबाउंडेड वाइल्डकार्ड। यह सभी प्रकार के परिवार के लिए खड़ा है। जब आप दोनों प्राप्त करें और उपयोग करें।
  • सह-विचरण: ? extends T(सभी प्रकार के परिवार जो उपप्रकार हैं T) - एक ऊपरी सीमा वाला एक वाइल्डकार्ड । Tहै ऊपरी वंशानुगत पदानुक्रम में अधिकांश वर्ग। extendsजब आप केवल किसी संरचना से मान प्राप्त करते हैं तो वाइल्डकार्ड का उपयोग करें ।
  • कॉन्ट्रा-विचरण: ? super T(सभी प्रकार के परिवार जो सुपरटेप के हैं T) - एक वाइल्डकार्ड जिसमें कम बाउंड होता हैTहै कम वंशानुगत पदानुक्रम में अधिकांश वर्ग। एक का प्रयोग करें superवाइल्डकार्ड जब आप केवल रखो एक संरचना में मान।

नोट: वाइल्डकार्ड का ?अर्थ है शून्य या एक बार , एक अज्ञात प्रकार का प्रतिनिधित्व करता है। वाइल्डकार्ड को एक पैरामीटर के प्रकार के रूप में इस्तेमाल किया जा सकता है, कभी भी जेनेरिक विधि इनवोकेशन के लिए एक प्रकार के तर्क के रूप में उपयोग नहीं किया जाता है, एक जेनेरिक क्लास इंस्टेंस निर्माण। (अर्थात जब वाइल्डकार्ड का उपयोग किया जाता है जो संदर्भ में उपयोग किए जाने वाले प्रोग्राम जैसे अन्य जगहों पर उपयोग नहीं किया जाता है 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.
        */  
    }
}

जेनरिक और उदाहरण


अरे, मैं सिर्फ यह जानना चाहता था कि आखिरी सेंस के साथ आपका क्या मतलब है: "अगर आपको लगता है कि मेरी सादृश्य गलत है तो कृपया अपडेट करें"। क्या इसका मतलब यह है कि अगर यह नैतिक रूप से गलत है (जो व्यक्तिपरक है) या अगर यह प्रोग्रामिंग के संदर्भ में गलत है (जो उद्देश्यपूर्ण है: नहीं, यह गलत नहीं है)? मैं इसे अधिक तटस्थ उदाहरण के साथ बदलना चाहूंगा जो सांस्कृतिक मानदंडों और नैतिक विश्वासों से सार्वभौमिक रूप से स्वीकार्य है; अगर वह आपके साथ ठीक है।
न्यूरॉन

आखिरकार मुझे मिल गया। अच्छी व्याख्या।
ओलेग कुट्स

2
@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.मैं तत्व को सूची <?> या सूची <में नहीं जोड़ सकता? ऑब्जेक्ट> का विस्तार करता है, इसलिए मुझे समझ नहीं आता कि यह क्यों हो सकता है Use when you both get and put
लियूवेनबिन_NO।

1
@LiuWenbin_NO। - जवाब का वह हिस्सा भ्रामक है। ?- "अनबाउंड वाइल्डकार्ड" - आक्रमण के ठीक विपरीत के साथ मेल खाता है। कृपया निम्नलिखित दस्तावेज देखें: docs.oracle.com/javase/tutorial/java/generics/… जो बताता है: उस स्थिति में जहां कोड को चर को "इन" और "आउट" चर दोनों के रूप में एक्सेस करने की आवश्यकता होती है, वाइल्डकार्ड का उपयोग न करें। (वे "प्राप्त" और "पुट" के पर्याय के रूप में "इन" और "आउट" का उपयोग कर रहे हैं। के अपवाद के साथ nullआप एक संग्रह में जोड़ नहीं सकते हैं जिसके साथ पैरामीटर किया गया है ?
मूसलैब्स

29
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
    }
}

तो "? फैली हुई बी" की व्याख्या "के रूप में की जानी चाहिए? बी फैली हुई है"। यह कुछ ऐसा है जो B का विस्तार करता है ताकि B को ऑब्जेक्ट के सभी सुपर क्लास शामिल होंगे, जिसमें B को छोड़कर। कोड के लिए धन्यवाद!
सौरभ पाटिल

3
@SaurabhPatil नहीं है, ? extends Bसाधन बी और कुछ भी बी का विस्तार
asgs

24

जैसा कि मैंने में समझाने मेरा उत्तर एक और सवाल करने के लिए, पी ई सी एस एक स्मरक मदद करने के लिए जोश बलोच द्वारा बनाई डिवाइस याद है पी roducer extends, सी onsumer super

इसका मतलब यह है कि जब एक विधि के लिए पारित किया जा रहा है एक पैरामीटर प्रकार के उदाहरणों का उत्पादन होगा T(वे इसे किसी तरह से फिर से प्राप्त किया जाएगा) का ? extends Tउपयोग किया जाना चाहिए, क्योंकि किसी उपवर्ग का कोई उदाहरण Tभी है T

जब एक विधि के लिए पारित किया जा रहा है एक पैरामीटर प्रकार के उदाहरणों का उपभोग करेगा T(वे कुछ करने के लिए इसे पारित किया जाएगा), ? super Tका उपयोग किया जाना चाहिए क्योंकि Tकानूनी तौर पर किसी भी विधि को पारित किया जा सकता है जो कुछ सुपरटेप को स्वीकार करता है T। एक Comparator<Number>एक पर इस्तेमाल किया जा सकता Collection<Integer>, उदाहरण के लिए। ? extends Tकाम नहीं करेगा, क्योंकि एक Comparator<Integer>पर काम नहीं कर सकता Collection<Number>

ध्यान दें कि आम तौर पर आप केवल का उपयोग कर किया जाना चाहिए ? extends Tऔर ? super Tकुछ विधि के मापदंडों के लिए। विधियों को केवल Tजेनेरिक रिटर्न प्रकार पर टाइप पैरामीटर के रूप में उपयोग करना चाहिए ।


1
क्या यह सिद्धांत केवल संग्रह के लिए है? यह समझ में आता है जब कोई एक सूची के साथ इसे सहसंबद्ध करने की कोशिश करता है। यदि आप सॉर्ट (सूची <T>, तुलनित्र <? सुपर टी>) ---> के हस्ताक्षर के बारे में सोचते हैं, तो यहां तुलनित्र सुपर का उपयोग करता है तो इसका मतलब है कि यह पीईसीएस संदर्भ में एक उपभोक्ता है। जब आप उदाहरण के लिए कार्यान्वयन को देखते हैं: सार्वजनिक int तुलना (व्यक्ति a, व्यक्ति b) {रिटर्न a.age <b.age? -1: a.age == b.age? 0: 1; } मुझे ऐसा लगता है कि व्यक्ति किसी चीज का उपभोग नहीं करता बल्कि उम्र पैदा करता है। यही मुझे भ्रमित करता है। क्या मेरे तर्क में कोई दोष है या PECS केवल संग्रह के लिए है?
फतह अर्सलान

23

संक्षेप में, पीईसीएस को याद रखने के तीन आसान नियम:

  1. <? extends T>यदि आपको Tकिसी संग्रह से प्रकार का ऑब्जेक्ट पुनर्प्राप्त करने की आवश्यकता है, तो वाइल्डकार्ड का उपयोग करें ।
  2. <? super T>यदि आपको Tसंग्रह में प्रकार की वस्तुओं को रखने की आवश्यकता है तो वाइल्डकार्ड का उपयोग करें ।
  3. यदि आपको दोनों चीजों को संतुष्ट करने की आवश्यकता है, तो ठीक है, किसी भी वाइल्डकार्ड का उपयोग न करें। कि जैसे ही आसान।

10

आइए मान लेते हैं यह पदानुक्रम:

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 की कोई भी वस्तु नहीं जोड़ सकते क्योंकि आप एक संयोजन के साथ समाप्त हो सकते हैं जो जावा में अनुमति नहीं है।
व्यवहार में, संकलक वास्तव में संकलन में देख सकता है कि आप एक बी जोड़ते हैं:

sharks.add(new HammerShark());

... लेकिन यह बताने का कोई तरीका नहीं है कि क्या रनटाइम के दौरान, आपका बी सूची प्रकार का उपप्रकार या सुपरटाइप होगा। रनटाइम के दौरान सूची प्रकार A, B, C. में से कोई भी हो सकता है। इसलिए आप उदाहरण के लिए Dead HammerShark की सूची में HammerSkark (सुपर प्रकार) को शामिल नहीं कर सकते हैं।

* आप कहेंगे: "ठीक है, लेकिन मैं इसमें HammerSkark नहीं जोड़ सकता क्योंकि यह सबसे छोटा प्रकार है।" उत्तर: यह सबसे छोटा है जिसे आप जानते हैं। लेकिन HammerSkark को किसी और के द्वारा बढ़ाया जा सकता है और आप उसी परिदृश्य में समाप्त हो सकते हैं।

आइए सीएस को स्पष्ट करें - उपभोक्ता सुपर:

उसी पदानुक्रम में हम यह कोशिश कर सकते हैं:

List<? super Shark> sharks = new ArrayList<>();

आप इस सूची में क्या और क्यों जोड़ सकते हैं?

sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());

आप उपरोक्त प्रकार की वस्तुओं को जोड़ सकते हैं क्योंकि शार्क (ए, बी, सी) के नीचे कुछ भी हमेशा शार्क (एक्स, वाई, जेड) के ऊपर किसी भी चीज का उपप्रकार होगा। समझने में आसान।

आप शार्क के ऊपर प्रकार नहीं जोड़ सकते हैं , क्योंकि रनटाइम में अतिरिक्त प्रकार की वस्तु सूची के प्रकार (एक्स, वाई, जेड) की तुलना में पदानुक्रम में अधिक हो सकती है। इसकी अनुमति नहीं है।

लेकिन आप इस सूची से क्यों नहीं पढ़ सकते हैं? (मेरा मतलब है कि आप इसमें से एक तत्व प्राप्त कर सकते हैं, लेकिन आप इसे ऑब्जेक्ट ओ के अलावा किसी भी चीज़ को नहीं दे सकते हैं):

Object o;
o = sharks.get(2);// only assignment that works

Animal s;
s = sharks.get(2);//doen't work

रनटाइम के समय, सूची का प्रकार A: X, Y, Z, ... से ऊपर का कोई भी प्रकार हो सकता है ... संकलक आपके असाइनमेंट स्टेटमेंट (जो सही लगता है) को संकलित कर सकता है, लेकिन रनटाइम के प्रकार (एनिमल) में निम्न हो सकता है सूची के घोषित प्रकार की तुलना में पदानुक्रम (जो क्रिएचर, या उच्चतर हो सकता है)। इसकी अनुमति नहीं है।

सारांश में

हम का उपयोग करें <? super T>जोड़ने के लिए प्रकार की वस्तुओं के बराबर या इससे नीचे Tकरने के लिए Listहम इससे नहीं पढ़ सकते।
हम सूची से <? extends T>समान या नीचे के प्रकारों की वस्तुओं को पढ़ने के लिए उपयोग करते हैं Tहम इसमें तत्व नहीं जोड़ सकते।


9

(एक जवाब जोड़ना क्योंकि जेनरिक वाइल्डकार्ड के साथ कभी पर्याप्त उदाहरण नहीं)

       // 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);
        }
    }

4

मेरे लिए यह सबसे स्पष्ट, सरलतम तरीका है जो सुपर बनाम फैली हुई है।

  • extendsपढ़ने के लिए है

  • superके लिए है लेखन

मुझे लगता है कि "निर्माता" कौन है और "उपभोक्ता" कौन है, इसके बारे में सोचने के लिए "पीईसीएस" एक गैर-स्पष्ट तरीका है। "पी ई सी एस" के नजरिए से परिभाषित किया गया है अपने आप डेटा संग्रह - संग्रह "खपत" यदि वस्तुओं लिखा जा रहा है करने के लिए यह (यह बुला कोड से वस्तुओं लेने वाली है), और यह "पैदा करता है" अगर वस्तुओं पढ़ा जा रहा है से (यह यह कुछ कॉलिंग कोड के लिए वस्तुओं का उत्पादन कर रहा है)। यह इस बात के लिए काउंटर है कि बाकी सब चीजों का नामकरण कैसे किया जाता है। मानक जावा एपीआई को कॉलिंग कोड के परिप्रेक्ष्य से नाम दिया गया है, न कि केवल संग्रह। उदाहरण के लिए, java.util.List के संग्रह-केंद्रित दृश्य में "ऐड ()" के बजाय "प्राप्त ()" नाम का एक तरीका होना चाहिए - आखिरतत्व, लेकिन सूची में ही तत्व प्राप्त होता है।

मुझे लगता है कि यह कोड के परिप्रेक्ष्य से चीजों के बारे में सोचने के लिए अधिक सहज, स्वाभाविक और सुसंगत है जो संग्रह के साथ बातचीत करता है - क्या कोड "से पढ़ा जाता है" या "संग्रह से लिखता है"? इसके बाद, संग्रह के लिए कोई भी कोड लिखना "निर्माता" होगा, और संग्रह से कोई भी कोड पढ़ना "उपभोक्ता" होगा।


मुझे लगता है कि एक ही मानसिक टक्कर आई है और इसके अलावा कि पी ई सी एस कोड के नामकरण और प्रकार सीमाओं खुद को निर्दिष्ट नहीं करता इस बात से सहमत करने के लिए जाते हैं कर रहे हैं संग्रह घोषणाओं पर निर्धारित किया है। इसके अलावा जहां तक नामकरण का सवाल है तो आप अक्सर उत्पादन / की तरह लेने वाली संग्रह के लिए नाम है srcऔर dst। तो आप एक ही समय में कोड और कंटेनर दोनों के साथ काम कर रहे हैं और मैंने उन पंक्तियों के साथ इसके बारे में सोचना शुरू कर दिया है - "खपत कोड" एक उत्पादक कंटेनर से खपत करता है, और एक खपत कंटेनर के लिए "उत्पादन कोड" का उत्पादन होता है।
मूसलैब्स

4

पीईसीएस "नियम" केवल यह सुनिश्चित करता है कि निम्नलिखित कानूनी है:

  • उपभोक्ता: जो कुछ भी ?है, वह कानूनी रूप से संदर्भित कर सकता है T
  • निर्माता: जो भी ?है, इसे कानूनी रूप से संदर्भित किया जा सकता है T

की पंक्तियों के साथ विशिष्ट युग्मन List<? extends T> producer, List<? super T> consumerकेवल यह सुनिश्चित कर रहा है कि संकलक मानक "आईएस-ए" विरासत संबंध नियमों को लागू कर सकता है। यदि हम कानूनी रूप से ऐसा कर सकते हैं, तो यह कहना सरल हो सकता है <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-ए ' - कि यह को सौंपा जा सकता है, क्योंकि के रूप में एक सुपर प्रकार (या अधिकांश एक ही प्रकार) है ।consumer?<? super T>?T?T?T

के लिए producerचिंता का विषय ही यह सिर्फ उल्टे होने वाली है: producerके ?वाइल्डकार्ड (अज्ञात प्रकार) है दिग्दर्शन - असाइनमेंट की "दाहिने हाथ की ओर" - और <? extends T>यह सुनिश्चित करें कि जो कुछ भी ?है, ?"एक है" T- कि यह सौंपा जा सकता है एक करने के लिएT , क्योंकि ?एक उप प्रकार (या कम से कम एक ही प्रकार) है T


2

यह याद रखना:

उपभोक्ता खाने रात का खाना (सुपर); निर्माता अपने माता-पिता के कारखाने का विस्तार करता है


1

वास्तविक जीवन उदाहरण का उपयोग करना (कुछ सरलीकरण के साथ):

  1. एक सूची के अनुरूप के रूप में माल ढुलाई कारों के साथ एक मालगाड़ी की कल्पना करें।
  2. आप माल को माल कार में रख सकते हैं यदि माल भाड़ा कार की तुलना में समान या छोटे आकार का है<? super FreightCarSize>
  3. यदि आपके पास अपने डिपो = में पर्याप्त जगह (कार्गो के आकार से अधिक) है, तो आप माल गाड़ी से माल उतार सकते हैं<? extends DepotSize>

1

सहप्रसरण : स्वीकार उपप्रकार
contravariance : supertypes स्वीकार

सहसंयोजक प्रकार केवल-पढ़ने के लिए होते हैं, जबकि contravariant प्रकार केवल-लेखन होते हैं।


0

उदाहरण पर एक नज़र डालते हैं

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);

मुसीबत

चूंकि जावा का संग्रह एक संदर्भ प्रकार है जिसके परिणामस्वरूप हमारे पास अगले मुद्दे हैं:

समस्या # 1

//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* स्विफ्ट की जेनेरिक में ऐसी समस्या नहीं है क्योंकि कलेक्शन Value type[अबाउट] इसलिए एक नया कलेक्शन बनाया गया है

समस्या # 2

//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

समाधान - जेनेरिक वाइल्डकार्ड

वाइल्डकार्ड एक संदर्भ प्रकार की विशेषता है और इसे सीधे इंस्टेंट नहीं किया जा सकता है

समाधान # 1 <? super 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>उर्फ अपर बाउंड उर्फ ​​कोवरियनस उर्फ ​​निर्माता गारंटी देता है कि यह ए और सभी उपवर्गों द्वारा संचालित होता है, यही कारण है कि इसे प्राप्त करना और डालना सुरक्षित है

List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;

//get
A a0 = listExtendsA.get(0);

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.