क्या अंतर है <? आधार> और <T बेस बढ़ाता है>?


29

इस उदाहरण में:

import java.util.*;

public class Example {
    static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    static void function(List<? extends Number> outer)
    {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

doesntCompile() के साथ संकलन करने में विफल:

Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
        doesntCompile(new HashMap<Integer, List<Integer>>());
                      ^

जबकि compiles() संकलक द्वारा स्वीकार किया जाता है।

यह उत्तर बताता है कि अंतर केवल यह है कि इसके विपरीत<? ...> , <T ...>आपको बाद में उस प्रकार का संदर्भ देता है, जो ऐसा प्रतीत नहीं होता है।

इस मामले में क्या अंतर है <? extends Number>और <T extends Number>पहला संकलन क्यों नहीं है?


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल ल्यू

[१] जावा जेनेरिक प्रकार: सूची के बीच अंतर <? संख्या> और सूची का विस्तार <टी का विस्तार संख्या> एक ही सवाल पूछते हुए प्रतीत होता है, लेकिन जब यह ब्याज की हो सकती है तो यह वास्तव में एक डुप्लिकेट नहीं है। [२] जबकि यह एक अच्छा प्रश्न है, शीर्षक ठीक से अंतिम वाक्य में पूछे जा रहे विशिष्ट प्रश्न को नहीं दर्शाता है।
स्कोमीसा


यहाँ स्पष्टीकरण के बारे में कैसे ?
मरोक

जवाबों:


14

निम्नलिखित हस्ताक्षर के साथ विधि को परिभाषित करके:

static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

और इसे लागू करना जैसे:

compiles(new HashMap<Integer, List<Integer>>());

That8.1.2 jls में हम पाते हैं, कि (मेरे द्वारा बोला गया दिलचस्प हिस्सा):

एक सामान्य श्रेणी की घोषणा, पैरामीटर प्रकारों ()4.5) के एक सेट को परिभाषित करती है, जो टाइप तर्कों द्वारा प्रकार पैरामीटर अनुभाग के प्रत्येक संभावित आह्वान के लिए है । इन सभी प्रकार के मानकीकृत रन समय में एक ही वर्ग साझा करते हैं।

दूसरे शब्दों में, टाइप Tको इनपुट प्रकार के विरुद्ध मिलान किया जाता है और असाइन किया जाता है Integer। हस्ताक्षर प्रभावी रूप से बन जाएगा static void compiles(Map<Integer, List<Integer>> map)

जब doesntCompileविधि की बात आती है , तो jls सबटाइपिंग के नियमों को परिभाषित करता है ( .14.5.1 , मेरे द्वारा बोल्ड किया गया):

एक प्रकार का तर्क T1 में एक अन्य प्रकार का तर्क T2, T2 <= T1 लिखा होता है, यदि T2 द्वारा निरूपित प्रकारों के समुच्चय को T1 द्वारा निरूपित प्रकार के समुच्चय का एक उपसमूह निम्नलिखित नियमों के तहत (और) बंद कर दिया जाता है ( जहाँ <: सबटाइपिंग को दर्शाता है (:4.10)):

  • ? टी <= T <: S का विस्तार करता है

  • ? टी <=

  • ? सुपर टी <=? सुपर एस अगर एस <: टी

  • ? सुपर टी <=?

  • ? सुपर टी <=? फैली हुई वस्तु

  • टी <= टी

  • टी <=? टी का विस्तार करता है

  • टी <=? सुपर टी

इस का मतलब है कि ? extends Numberवास्तव में होता है Integerया यहां तक कि List<? extends Number>होता है List<Integer>, लेकिन इसके लिए ऐसा नहीं है Map<Integer, List<? extends Number>>और Map<Integer, List<Integer>>। उस SO थ्रेड में उस विषय पर अधिक जानकारी प्राप्त की जा सकती है । आप अभी भी ?घोषित करके वाइल्डकार्ड कार्य के साथ संस्करण बना सकते हैं , जिससे आप यह उम्मीद कर सकते हैं List<? extends Number>:

public class Example {
    // now it compiles
    static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    public static void main(String[] args) {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

[१] मुझे लगता है कि आप के ? extends Numberबजाय इसका मतलब है ? extends Numeric। [२] आपका दावा है कि "यह सूची के लिए मामला नहीं है <; संख्या बढ़ाता है> और सूची <पूर्णांक>" गलत है। के रूप में @VinceEmigh पहले ही बताया गया है, आप के लिए एक विधि बना सकते हैं static void demo(List<? extends Number> lst) { }और इसे इस तरह कहते हैं demo(new ArrayList<Integer>());या यह demo(new ArrayList<Float>());, और कोड compiles और रन ठीक है। या क्या मैं गलत या गलत समझ रहा हूं कि आपने क्या कहा?
स्कोमीसा

@ आप दोनों मामलों में सही हैं। जब यह आपके दूसरे बिंदु पर आता है, तो मैंने इसे भ्रामक तरीके से लिखा। मेरा मतलब List<? extends Number>पूरे नक्शे के एक प्रकार के पैरामीटर के रूप में था , न कि स्वयं। टिप्पणी के लिए आपका बहुत-बहुत धन्यवाद।
एन्ड्रॉनिकस

@skomisa उसी कारण से List<Number> शामिल नहीं है List<Integer>। मान लीजिए आपका कोई फंक्शन है static void check(List<Number> numbers) {}। जब check(new ArrayList<Integer>());इसे लागू करना संकलित नहीं करता है, तो आपको विधि को परिभाषित करना होगा static void check(List<? extends Number> numbers) {}। नक्शे के साथ यह समान है लेकिन अधिक घोंसले के शिकार के साथ।
एन्ड्रॉनिकस

1
@skomisa बस Numberसूची का एक प्रकार का पैरामीटर है और आपको ? extendsइसे covariant बनाने के लिए जोड़ना होगा,List<? extends Number> एक प्रकार का पैरामीटर है Mapऔर ? extendscovariance की भी आवश्यकता है।
एंड्रोनिकस

1
ठीक। चूंकि आपने बहु स्तरीय वाइल्डकार्ड (उर्फ) के लिए समाधान प्रदान किया है "नेस्टेड वाइल्डकार्ड" ?) के , और प्रासंगिक जेएलएस संदर्भ से जुड़ा है, तो इनाम है।
skomisa

6

कॉल में:

compiles(new HashMap<Integer, List<Integer>>());

टी को इंटेगर से मिलाया जाता है, इसलिए तर्क का प्रकार ए है Map<Integer,List<Integer>>। यह विधि के लिए मामला नहीं है doesntCompile: तर्क का प्रकार Map<Integer, List<? extends Number>>कॉल में वास्तविक तर्क जो भी हो; और वह से असाइन करने योग्य नहीं है HashMap<Integer, List<Integer>>

अपडेट करें

में doesntCompileविधि, कुछ भी आप इस तरह कुछ करने के लिए होने से बचाता है:

static void doesntCompile(Map<Integer, List<? extends Number>> map) {
    map.put(1, new ArrayList<Double>());
}

तो जाहिर है, यह एक HashMap<Integer, List<Integer>>तर्क के रूप में स्वीकार नहीं कर सकता ।


एक वैध कॉल की doesntCompileतरह, तब क्या होगा ? बस उसी को लेकर उत्सुक हूं।
Xtreme बाइकर

1
@XtremeBiker doesntCompile(new HashMap<Integer, List<? extends Number>>());काम करेगा, जैसा होगा doesntCompile(new HashMap<>());
स्कोमीसा

@XtremeBiker, यहां तक ​​कि यह काम करेगा, नक्शा <पूर्णांक, सूची <? फैली हुई संख्या >> मानचित्र = नया हैशपेयर <पूर्णांक, सूची <? विस्तार संख्या >> (); map.put (अशक्त, नया एरियरिस्ट <Integer> ()); doesntCompile (नक्शा);
मूनी

"जो कि इससे असाइन करने योग्य नहीं है HashMap<Integer, List<Integer>>" क्या आप इसे विस्तृत रूप से बता सकते हैं कि यह इससे क्यों उपलब्ध नहीं है?
देव नल

@DevNull ऊपर मेरा अपडेट देखें
मौरिस पेरी

2

प्रदर्शन का सरल उदाहरण। उसी उदाहरण को नीचे की तरह कल्पना की जा सकती है।

static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too

public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}

List<Pair<? extends Number>> एक बहु-स्तरीय वाइल्डकार्ड प्रकार है जबकि List<? extends Number> एक मानक वाइल्डकार्ड प्रकार है।

वाइल्ड कार्ड प्रकार के ठोस ठोस तात्कालिकता में List<? extends Number>शामिल हैं Numberऔर किसी भी उपप्रकार के Numberमामले मेंList<Pair<? extends Number>> एक प्रकार का तर्क है और स्वयं में सामान्य प्रकार का एक ठोस तात्कालिकता है।

जेनरिक अपरिवर्तनीय हैं इसलिए Pair<? extends Number>वाइल्ड कार्ड प्रकार केवल स्वीकार कर सकते हैं Pair<? extends Number>>। भीतरी प्रकार ? extends Numberपहले से ही सहसंयोजक है। आपको सहसंयोजक की अनुमति देने के लिए कोवेरेंट के रूप में संलग्नक प्रकार बनाना होगा।


कैसे यह है कि <Pair<Integer>>साथ काम नहीं करता <Pair<? extends Number>>है, लेकिन साथ काम करता है <T extends Number> <Pair<T>>?
jaco0646

@ jaco0646 आप अनिवार्य रूप से ओपी के रूप में एक ही सवाल पूछ रहे हैं, और एंड्रॉनिकस से जवाब स्वीकार किया गया है। उस उत्तर में कोड उदाहरण देखें।
स्कोमीसा

@skomisa, हाँ, मैं कुछ कारणों से एक ही प्रश्न पूछ रहा हूँ: एक यह है कि यह उत्तर वास्तव में ओपी के प्रश्न को संबोधित नहीं करता है; लेकिन दो यह है कि मुझे यह उत्तर समझना आसान है। मैं किसी भी तरह से एंड्रॉनिकस के उत्तर का पालन नहीं कर सकता, जो मुझे नेस्टेड बनाम नॉन-नेस्टेड जेनरिक, या यहां तक ​​कि Tबनाम समझने की ओर ले जाता है ?। समस्या का एक हिस्सा यह है कि जब एंड्रॉनिकस अपने स्पष्टीकरण के महत्वपूर्ण बिंदु पर पहुंचता है, तो वह एक और धागे का बचाव करता है जो केवल तुच्छ उदाहरणों का उपयोग करता है। मैं यहां एक स्पष्ट और अधिक पूर्ण उत्तर प्राप्त करने की उम्मीद कर रहा था।
jaco0646

1
@ jaco0646 ठीक है। एंजेलिका लैंगर के दस्तावेज़ "जावा जेनरिक एफएक्यूज़ - टाइप आर्ग्युमेंट्स" में बहु-स्तरीय (यानी, नेस्टेड) ​​वाइल्डकार्ड्स शीर्षक वाले एफएक्यू है? । ओपी के सवाल में उठाए गए मुद्दों को समझाने के लिए मुझे पता है कि सबसे अच्छा स्रोत है। नेस्टेड वाइल्डकार्ड के नियम न तो सीधे हैं और न ही सहज हैं।
स्कोमीसा

1

मैं आपको जेनेरिक वाइल्डकार्ड के प्रलेखन में विशेष रूप से वाइल्डकार्ड उपयोग के लिए दिशानिर्देशों को देखने की सलाह दूंगा

खुलकर अपनी विधि #doesntCompile बोल रहा हूँ

static void doesntCompile(Map<Integer, List<? extends Number>> map) {}

और कॉल करें

doesntCompile(new HashMap<Integer, List<Integer>>());

मौलिक रूप से गलत है

आइए कानूनी कार्यान्वयन जोड़ें :

    static void doesntCompile(Map<Integer, List<? extends Number>> map) {
        List<Double> list = new ArrayList<>();
        list.add(0.);
        map.put(0, list);
    }

यह वास्तव में ठीक है, क्योंकि डबल संख्या का विस्तार करता है, इसलिए पुट List<Double>बिल्कुल ठीक है और साथ ही List<Integer>, ठीक है?

हालाँकि, क्या आपको लगता है कि आपके उदाहरण से यहाँ से गुजरना कानूनी है new HashMap<Integer, List<Integer>>()?

कंपाइलर ऐसा नहीं सोचता, और ऐसी परिस्थितियों से बचने के लिए अपना (इसका?) सबसे अच्छा कर रहा है।

विधि के साथ समान कार्यान्वयन करने का प्रयास करें # कंपाइल और संकलक स्पष्ट रूप से आपको नक्शे में डबल्स की सूची डालने की अनुमति नहीं देगा।

    static <T extends Number> void compiles(Map<Integer, List<T>> map) {
        List<Double> list = new ArrayList<>();
        list.add(10.);
        map.put(10, list); // does not compile
    }

मूल रूप से आप कुछ भी नहीं रख सकते हैं, लेकिन List<T>के कारण है कि यह सुरक्षित है के साथ कि विधि कॉल करने कि new HashMap<Integer, List<Integer>>()या new HashMap<Integer, List<Double>>()या new HashMap<Integer, List<Long>>()या new HashMap<Integer, List<Number>>()

तो संक्षेप में, आप संकलक के साथ धोखा करने की कोशिश कर रहे हैं और यह इस तरह की धोखाधड़ी के खिलाफ काफी बचाव करता है।

NB: मौरिस पेरी द्वारा पोस्ट किया गया उत्तर बिल्कुल सही है। मुझे यकीन नहीं है कि यह पर्याप्त स्पष्ट है, इसलिए अधिक व्यापक पोस्ट को जोड़ने के लिए कोशिश की गई (वास्तव में मुझे आशा है कि मैं इसमें कामयाब रहा)।

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