कोविरियन, इनवेरियन और कंट्रावेरियन ने सादे अंग्रेजी में समझाया?


113

आज, मैंने जावा में Covariance, Contravariance (और Invariance) के बारे में कुछ लेख पढ़े। मैंने अंग्रेजी और जर्मन विकिपीडिया लेख और आईबीएम के कुछ अन्य ब्लॉग पोस्ट और लेख पढ़े।

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

इसलिए मैं सादे अंग्रेजी में एक आसान स्पष्टीकरण की तलाश कर रहा हूं, जो एक शुरुआत दिखाता है कि कोवरियन और कंट्रावेरियन (और इनवेरियन) क्या है। एक आसान उदाहरण के लिए प्लस पॉइंट।


कृपया इस पोस्ट का संदर्भ लें, यह आपके लिए उपयोगी हो सकता है: stackoverflow.com/q/2501023/218717
फ्रांसिस्को अल्वारादो

3
शायद बेहतर एक प्रोग्रामर का स्टैक एक्सचेंज प्रकार प्रश्न। यदि आप वहां पोस्ट करते हैं, तो आप जो समझ रहे हैं, उसे बताते हुए विचार करें, और विशेष रूप से आपको भ्रमित करता है, क्योंकि अभी आप किसी से पूछ रहे हैं कि वह आपके लिए एक संपूर्ण ट्यूटोरियल फिर से लिखें।
होल्सक्राफ्ट ईल्स

जवाबों:


288

कुछ कहते हैं कि यह प्रकार और उप-प्रकारों के बीच संबंध के बारे में है, अन्य कहते हैं कि यह प्रकार रूपांतरण के बारे में है और अन्य लोग कहते हैं कि इसका उपयोग यह तय करने के लिए किया जाता है कि कोई विधि अधिलेखित है या अतिभारित है।

ऊपर के सभी।

दिल में, इन शब्दों का वर्णन है कि प्रकार परिवर्तन से उपप्रकार संबंध कैसे प्रभावित होता है। यह है, अगर Aऔर Bप्रकार हैं, fएक प्रकार का परिवर्तन है, और ype उपप्रकार संबंध (अर्थात A ≤ Bइसका Aएक उपप्रकार है B), हमारे पास है

  • fसहसंयोजक है अगर A ≤ Bइसका तात्पर्य हैf(A) ≤ f(B)
  • fगर्भनिरोधक है अगर A ≤ Bइसका मतलब हैf(B) ≤ f(A)
  • f उपरोक्त में से कोई भी नहीं है, तो अपरिवर्तनीय है

आइए एक उदाहरण पर विचार करें। चलो f(A) = List<A>जहां Listसे घोषित किया जाता है

class List<T> { ... } 

क्या fसहसंयोजक, कंट्रावेरिएंट, या अपरिवर्तनीय है? Covariant मतलब यह होगा कि एक List<String>के एक उप-प्रकार है List<Object>, contravariant है कि एक List<Object>के एक उप-प्रकार है List<String>और अपरिवर्तनीय है कि न तो अन्य की एक उप-प्रकार है, यानी List<String>और List<Object>अपरिवर्तनीय प्रकार हैं। जावा में, उत्तरार्द्ध सत्य है, हम कहते हैं (कुछ अनौपचारिक) कि जेनरिक अपरिवर्तनीय हैं।

एक और उदाहरण। चलो f(A) = A[]। क्या fसहसंयोजक, कंट्रावेरिएंट, या अपरिवर्तनीय है? यही है, स्ट्रिंग [] वस्तु का एक उपप्रकार [], वस्तु [] स्ट्रिंग का एक उपप्रकार [] है, या न ही दूसरे का उपप्रकार है? (उत्तर: जावा में, सरणियाँ सहसंयोजक हैं)

यह अभी भी अमूर्त था। इसे और अधिक ठोस बनाने के लिए, आइए देखें कि जावा में कौन से ऑपरेशन को उपप्रकार संबंध के संदर्भ में परिभाषित किया गया है। सबसे सरल उदाहरण असाइनमेंट है। बयान

x = y;

संकलन करेगा यदि typeof(y) ≤ typeof(x)। यही है, हमने सिर्फ बयानों को सीखा है

ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();

जावा में संकलित नहीं करेंगे, लेकिन

Object[] objects = new String[1];

मर्जी।

एक और उदाहरण जहां उपप्रकार संबंध मायने रखता है एक विधि आह्वान अभिव्यक्ति है:

result = method(a);

अनौपचारिक रूप से कहें तो, इस कथन का मूल्यांकन aविधि के पहले पैरामीटर के मान को निर्दिष्ट करके , फिर विधि के निकाय को निष्पादित करने के लिए किया जाता है, और फिर विधियों को वापस करने के लिए मान प्रदान किया जाता है result। अंतिम उदाहरण में सादे असाइनमेंट की तरह, "राइट हैंड साइड" "लेफ्ट हैंड साइड" का एक उपप्रकार होना चाहिए, अर्थात यह स्टेटमेंट केवल तभी मान्य हो सकता है यदि typeof(a) ≤ typeof(parameter(method))और returntype(method) ≤ typeof(result)। अर्थात्, यदि विधि द्वारा घोषित किया गया है:

Number[] method(ArrayList<Number> list) { ... }

निम्नलिखित में से कोई भी अभिव्यक्ति संकलित नहीं होगी:

Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());

परंतु

Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());

मर्जी।

एक और उदाहरण जहां मामलों को घटाया जाना ओवरराइडिंग है। विचार करें:

Super sup = new Sub();
Number n = sup.method(1);

कहाँ पे

class Super {
    Number method(Number n) { ... }
}

class Sub extends Super {
    @Override 
    Number method(Number n);
}

अनौपचारिक रूप से, रनटाइम इसे फिर से लिखेगा:

class Super {
    Number method(Number n) {
        if (this instanceof Sub) {
            return ((Sub) this).method(n);  // *
        } else {
            ... 
        }
    }
}

संकलित करने के लिए चिह्नित लाइन के लिए, ओवरराइडिंग विधि की विधि पैरामीटर को ओवरराइड विधि के विधि पैरामीटर का एक सुपरपाइप होना चाहिए, और ओवरराइड विधि के एक का उपप्रकार टाइप करें। औपचारिक रूप से, f(A) = parametertype(method asdeclaredin(A))कम से कम कंट्राविरेंट f(A) = returntype(method asdeclaredin(A))होना चाहिए , और यदि कम से कम सहसंयोजक होना चाहिए।

ऊपर "कम से कम" नोट करें। वे न्यूनतम आवश्यकताएं हैं जो किसी भी उचित वैधानिक रूप से सुरक्षित ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग भाषा लागू करेंगी, लेकिन एक प्रोग्रामिंग भाषा अधिक सख्त होने का चुनाव कर सकती है। जावा १.४ के मामले में, पैरामीटर और प्रकार के रिटर्न प्रकार एक जैसे होने चाहिए (जब ओवरराइड करने के तरीके को छोड़कर), जब ओवरराइडिंग के तरीके, यानी parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))ओवरराइडिंग। जावा 1.5 के बाद से, ओवरराइड करते समय सहसंयोजक वापसी प्रकारों की अनुमति है, अर्थात निम्नलिखित निम्नलिखित जावा 1.5 में संकलित होगा, लेकिन जावा एक्स में नहीं:

class Collection {
    Iterator iterator() { ... }
}

class List extends Collection {
    @Override 
    ListIterator iterator() { ... }
}

मुझे आशा है कि मैंने सब कुछ कवर किया - या बल्कि, सतह को खरोंच कर दिया। फिर भी मुझे उम्मीद है कि यह अमूर्त, लेकिन प्रकार के विचरण की महत्वपूर्ण अवधारणा को समझने में मदद करेगा।


1
ओवरराइड करते समय जावा 1.5 कॉन्ट्रैरिएंट तर्क प्रकारों की अनुमति दी जाती है। मुझे लगता है कि तुम चूक गए।
ब्रायन गॉर्डन

13
क्या वो? मैंने सिर्फ इसे ग्रहण करने की कोशिश की, और कंपाइलर ने सोचा कि मेरा मतलब ओवरराइड करने के बजाय ओवरलोड करना है, और जब मैंने सबक्लास पद्धति पर @ ऑवरराइड एनोटेशन रखा तो कोड को अस्वीकार कर दिया। क्या आपके पास अपने दावे के लिए कोई सबूत है कि जावा कंट्रावैरेंट तर्क प्रकारों का समर्थन करता है?
मेरिटॉन

1
आह, तुम सही हो। मेरा मानना ​​है कि किसी ने इसे बिना जांचे-परखे।
ब्रायन गॉर्डन

1
मैंने बहुत सारे दस्तावेज पढ़े और इस विषय पर कुछ वार्ताएँ देखीं लेकिन यह अब तक का सबसे अच्छा विवरण है। बहुत कुछ।
minzchickenflavor

1
+1 बिल्कुल निस्तेज और सरल होने के लिए A ≤ B। यह संकेतन चीजों को बहुत अधिक सरल और सार्थक बनाता है। अच्छा पढ़ें ...
रोमियो सिएरा

12

जावा प्रकार प्रणाली, और फिर कक्षाएं लेना:

किसी प्रकार की किसी भी वस्तु को T के उप-प्रकार की वस्तु के साथ प्रतिस्थापित किया जा सकता है।

टाइप वैरियन्स - क्लास मेथोलॉजियों की पूरी अवधारणाएँ हैं

class A {
    public S f(U u) { ... }
}

class B extends A {
    @Override
    public T f(V v) { ... }
}

B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;

यह देखा जा सकता है:

  • T को उप-प्रकार S ( सहसंयोजक होना चाहिए , क्योंकि B A का उप-प्रकार है )।
  • V को U का संदर्भ होना चाहिए ( गर्भनिरोधक , जैसे कि गर्भनिरोधक दिशा)।

अब सह और गर्भनिरोधक- बी के सबटाइप से संबंधित हैं। निम्नलिखित मजबूत टाइपिंग को और अधिक विशिष्ट ज्ञान के साथ पेश किया जा सकता है। उपप्रकार में।

Covariance (जावा में उपलब्ध) यह कहना उपयोगी है कि एक उप-प्रकार में अधिक विशिष्ट परिणाम देता है; विशेष रूप से तब देखा जाता है जब ए = टी और बी = एस। कंट्रोवर्सी का कहना है कि आप अधिक सामान्य तर्क को संभालने के लिए तैयार हैं।


8

विभिन्न जनरलों के मापदंडों के साथ वर्गों के बीच रिश्तों के बारे में भिन्नता है। उनके रिश्ते ही यही कारण हैं कि हम उन्हें कास्ट कर सकते हैं।

सह और कॉन्ट्रा विचरण काफी तार्किक चीजें हैं। भाषा प्रकार प्रणाली हमें वास्तविक जीवन तर्क का समर्थन करने के लिए मजबूर करती है। उदाहरण द्वारा समझना आसान है।

सहप्रसरण

उदाहरण के लिए आप एक फूल खरीदना चाहते हैं और आपके शहर में दो फूलों की दुकान है: गुलाब की दुकान और डेज़ी की दुकान।

यदि आप किसी से पूछते हैं "फूलों की दुकान कहाँ है?" और कोई आपको बताता है कि गुलाब की दुकान कहां है, क्या यह ठीक होगा? हां, क्योंकि गुलाब एक फूल है, अगर आप एक फूल खरीदना चाहते हैं तो आप गुलाब खरीद सकते हैं। वही लागू होता है यदि किसी ने आपको डेज़ी शॉप के पते के साथ उत्तर दिया। यह सहसंयोजकता का उदाहरण है : आपको वहां A<C>जाने की अनुमति है A<B>, जहां , यदि, Cउप-वर्ग हैBA (समारोह से एक परिणाम के रूप रिटर्न) सामान्य मूल्यों पैदा करता है। Covariance निर्माताओं के बारे में है।

प्रकार:

class Flower {  }
class Rose extends Flower { }
class Daisy extends Flower { }

interface FlowerShop<T extends Flower> {
    T getFlower();
}

class RoseShop implements FlowerShop<Rose> {
    @Override
    public Rose getFlower() {
        return new Rose();
    }
}

class DaisyShop implements FlowerShop<Daisy> {
    @Override
    public Daisy getFlower() {
        return new Daisy();
    }
}

प्रश्न "फूलों की दुकान कहाँ है?", उत्तर "गुलाब की दुकान है":

static FlowerShop<? extends Flower> tellMeShopAddress() {
    return new RoseShop();
}

contravariance

उदाहरण के लिए आप अपनी प्रेमिका को फूल भेंट करना चाहते हैं। यदि आप प्रेमिका को किसी फूल से प्यार करते हैं, तो क्या आप उसे एक व्यक्ति के रूप में मान सकते हैं जो गुलाब से प्यार करता है, या एक ऐसे व्यक्ति के रूप में जो प्यार करता है? हाँ, क्योंकि अगर वह किसी भी फूल से प्यार करती है तो वह गुलाब और डेज़ी दोनों से प्यार करती है। यह विरोधाभासी का एक उदाहरण है : यदि आप , जहां , उप-वर्ग में है , तो आपको वहां A<B>जाने की अनुमति हैA<C>CBA खपत सामान्य मूल्य। उपभोक्ताओं के बारे में विरोधाभासी है।

प्रकार:

interface PrettyGirl<TFavouriteFlower extends Flower> {
    void takeGift(TFavouriteFlower flower);
}

class AnyFlowerLover implements PrettyGirl<Flower> {
    @Override
    public void takeGift(Flower flower) {
        System.out.println("I like all flowers!");
    }

}

आप अपनी प्रेमिका पर विचार कर रहे हैं जो किसी भी फूल को प्यार करती है जैसे कि वह जो गुलाब से प्यार करता है, और उसे गुलाब दे:

PrettyGirl<? super Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());

आप स्रोत पर अधिक पा सकते हैं ।


@Peter, धन्यवाद, यह एक उचित बिंदु है। यह तब होता है जब विभिन्न जेनेरिक मापदंडों वाले वर्गों के बीच कोई संबंध नहीं होता है, अर्थात आप A <B> को A <C> जो B और C के बीच का संबंध है, नहीं डाल सकते हैं।
वाडजिमवी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.