2015-11-23 3 views
14

मैं समझने की कोशिश कर रहा हूं कि इस कोड में अनचेक कास्ट चेतावनी क्यों है। पहले दो डाले कोई चेतावनी है, लेकिन तीसरे करता है:जेनेरिक क्लास लागू करने के लिए अनचेक कास्ट मानचित्र <String, V>

प्रकार सुरक्षा:

class StringMap<V> extends HashMap<String, V> { 
} 

class StringToIntegerMap extends HashMap<String, Integer> { 
} 

Map<?, ?> map1 = new StringToIntegerMap(); 
if (map1 instanceof StringToIntegerMap) { 
    StringToIntegerMap stringMap1 = (StringToIntegerMap)map1; //no unchecked cast warning 
} 

Map<String, Integer> map2 = new StringMap<>(); 
if (map2 instanceof StringMap) { 
    StringMap<Integer> stringMap2 = (StringMap<Integer>)map2; //no unchecked cast warning 
} 

Map<?, Integer> map3 = new StringMap<>(); 
if (map3 instanceof StringMap) { 
    StringMap<Integer> stringMap3 = (StringMap<Integer>)map3; //unchecked cast warning 
} 

यह stringMap3 कलाकारों के लिए पूर्ण चेतावनी है अनियंत्रित StringMap<Integer>

को Map<capture#3-of ?,Integer> से डाली

हालांकि, StringMap वर्ग घोषणा Map (यानी, String) का पहला प्रकार पैरामीटर निर्दिष्ट करती है, औरदोनोंऔर StringMap<Integer> कास्ट Map (यानी Integer) के दूसरे प्रकार के पैरामीटर के लिए उसी प्रकार का उपयोग करें। जो मैं समझता हूं, जब तक कि कास्ट ClassCastException नहीं फेंकता है (और ऐसा नहीं होना चाहिए क्योंकि instanceof चेक है), stringMap3 मान्य Map<String, Integer> होगा।

क्या यह जावा कंपाइलर की एक सीमा है? या क्या कोई परिदृश्य है जहां कुछ तर्कों के साथ Map3 या stringMap3 के कॉलिंग विधियों का परिणाम अनपेक्षित ClassCastException हो सकता है यदि चेतावनी को अनदेखा किया जाता है?

उत्तर

8

व्यवहार निर्दिष्ट किया जाता है के रूप में।Section 5.5.2 of the Java Language Specification में एक अनियंत्रित डाली परिभाषित किया गया है के रूप में:

एक पैरामिट्रीकृत प्रकार T करने के लिए एक प्रकार S से एक डाली अनियंत्रित है जब तक कि निम्न में से कम से कम एक सत्य है:

  • S <: T

  • T के सभी प्रकार के तर्क असंबद्ध वाइल्डकार्ड

  • T <: S और S में XT के अलावा कोई उप प्रकार है जहां X के प्रकार तर्क T के प्रकार तर्कों में निहित नहीं हैं।

(जहां A <: B अर्थ है: "AB की एक उप-प्रकार है")।

आपके पहले उदाहरण में, लक्ष्य प्रकार में कोई वाइल्डकार्ड नहीं है (और इस प्रकार उनमें से सभी असंबद्ध हैं)। आपके दूसरे उदाहरण में, StringMap<Integer> वास्तव में Map<String, Integer> का एक उप प्रकार है (और तीसरी स्थिति में वर्णित कोई उप प्रकार X नहीं है)।

आपके तीसरे उदाहरण में, हालांकि, आपके पास Map<?, Integer> से StringMap<Integer> तक कास्ट है, और वाइल्डकार्ड ? की वजह से, न तो दूसरे का उप प्रकार है। इसके अलावा, जाहिर है, सभी प्रकार के पैरामीटर असंबद्ध वाइल्डकार्ड नहीं हैं, इसलिए कोई भी स्थिति लागू नहीं होती है: यह एक अनचेक अपवाद है।

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

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

+0

यह निष्कर्ष था कि मैं @ पित्ता के जवाब और जेएलएस के विभिन्न वर्गों को पढ़ने के बाद आ रहा था। मुझे लगता है कि जेएलएस को दो पैरामीटर प्रकारों के बीच बेहतर कास्टिंग नियम जोड़ने के लिए अद्यतन किया जा सकता है, जहां संबंधित जेनेरिक वर्ग अलग-अलग प्रकार के पैरामीटर का उपयोग करते हैं, लेकिन मुझे नहीं पता कि यह कितना व्यवहार्य है या साइड इफेक्ट्स। यह प्रश्न तब तक संबंधित प्रतीत होता है जब तक कि मैं गलत नहीं हूं: [जेनेरिक वर्ग के जेनेरिक उपप्रकारों को कास्ट करना] (// stackoverflow.com/questions/17883536/casting-to-generic-subtypes-of-a-generic-class) । – blurredd

3

यह कास्ट सुरक्षित नहीं है। आइए मान लें कि आपके पास है:

Map<?, Integer> map3 = new HashMap<String,Integer>(); 
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3; 

यह एक अपवाद फेंकने जा रहा है। इससे कोई फर्क नहीं पड़ता कि आप जानते हैं कि आपने StringMap<Integer> को नया किया है और इसे map3 पर असाइन किया है। अधिक जानकारी के लिए आप जो कर रहे हैं उसे डाउन-कास्टिंग Downcasting in Java के रूप में जाना जाता है।

संपादित करें: आप भी भर जेनरिक के साथ समस्या यह उलझी रहे हैं, आप किसी भी सामान्य प्रकार के बिना ठीक उसी मुद्दा होगा।

+0

बिल्कुल वही चीज़ मिल जाएगी। अनिवार्य रूप से, आप "मैप " को "हैश मैप <स्ट्रिंग, इंटीजर>" में डालने की कोशिश कर रहे हैं, तो एक? एक स्ट्रिंग में ... – JayC667

+0

जेनेरिक का उपयोग प्रश्न के लिए केंद्रीय है। "सुरक्षित" से, मेरा मतलब था कि मैप 3 या स्ट्रिंग मैप 3 के लिए कुछ तर्कों के साथ एक विधि (या तरीकों का एक सेट) कॉल करना संभव था, अगर चेतावनी को अनदेखा किया गया तो अप्रत्याशित क्लासकास्ट अपवाद का कारण बन जाएगा। मैं नहीं पूछ रहा था कि StringMap की प्रारंभिक कलाकार क्लासकास्ट अपवाद का कारण बन सकती है या नहीं। मैंने सवाल अपडेट किया। – blurredd

+1

@ पॉलबोडिंगटन आप सही हैं। माफी –

3

वास्तव में उत्तर जावा भाषा विशिष्टता में है। Section 5.1.10 उल्लेख करता है कि यदि आप वाइल्डकार्ड का उपयोग करते हैं, तो यह एक नया कैप्चर प्रकार होगा।

यह संकेत मिलता है कि, Map<String, Integer>Map<?, Integer> का एक उपवर्ग भले ही StringMap<Integer> एक Map<?, Integer> प्रकार के आबंटित है नहीं है, और इसलिए क्योंकि Map<?, Integer> कुछ प्रमुख प्रकार और पूर्णांक मान है, जो StringMap<Integer> को सच है, कलाकारों के साथ एक नक्शा का प्रतिनिधित्व करता है असुरक्षित है तो यही कारण है कि आप एक अनचेक कास्ट चेतावनी प्राप्त करते हैं।

काम के बीच देखने के संकलक बिंदु से, और कलाकारों वहाँ कुछ भी हो सकता है, तो भले ही map3 डाली आपरेशन में StringMap<Integer> का एक उदाहरण है, map3 Map<?, Integer> अपने प्रकार के रूप में है, तो चेतावनी पूरी तरह से कानूनी है।

और अपने प्रश्न के लिए जवाब देने के लिए: हाँ, अपने कोड संभवतः कि गारंटी नहीं दे सकता है जब तक map3 उदाहरण केवल कुंजी के रूप में तार है, और।

क्यों नहीं देखें:

Map<?, Integer> map3 = new StringMap<Integer>(); 

// valid operation to use null as a key. Possible NPE depending on the map implementation 
// you choose as superclass of the StringMap<V>. 
// if you do not cast map3, you can use only null as the key. 
map3.put(null, 2); 

// but after an other unchecked cast if I do not want to create an other storage, I can use 
// any key type in the same map like it would be a raw type. 
((Map<Double, Integer>) map3).put(Double.valueOf(0.3), 2); 

// map3 is still an instance of StringMap 
if (map3 instanceof StringMap) { 
    StringMap<Integer> stringMap3 = (StringMap<Integer>) map3; // unchecked cast warning 
    stringMap3.put("foo", 0); 
    for (String s : stringMap3.keySet()){ 
     System.out.println(s+":"+map3.get(s)); 
    } 
} 

परिणाम:

null:2 
foo:0 
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String 
+0

ओह, और वैसे, कोई भी इस तरह के पहले संस्करण का दुरुपयोग कर सकता है, जिसके लिए एक अन्य अनचेक कलाकार की आवश्यकता होती है। और जब तक आप उस अनचेक कास्ट नहीं करते हैं, तो आप केवल नक्शा की पुल विधि का उपयोग कुंजी के रूप में शून्य के साथ कर सकते हैं, और मूल्य के रूप में शून्य, जो एक तरह से सुरक्षित है, क्योंकि यदि नक्शा कार्यान्वयन शून्य कुंजी और शून्य मानों का समर्थन करता है, तो आपके पढ़ने के संचालन को मानचित्र में संभावित शून्य कुंजी और शून्य मान पर विचार करना चाहिए। – pifta

+0

मेरा मतलब यह नहीं था कि 'स्ट्रिंग मैप ' के अलावा अन्य अनचेक किए गए अन्य स्थान होंगे। आप संभावित रूप से किसी भी मानचित्र को भ्रष्ट कर सकते हैं - या उस मामले के लिए कोई सामान्य वस्तु - सही अनचेक कास्ट के साथ, इसलिए आपका उदाहरण जवाब नहीं देता है कि 'मानचित्र ' से 'स्ट्रिंग मैप ' पर कास्टिंग कभी भी ढेर प्रदूषण को विपरीत नहीं कह सकता है , 'मानचित्र ' से 'मानचित्र <बूलियन, इंटीजर>' से कास्टिंग। आप एक परिदृश्य बना सकते हैं जहां 'map3' को असाइन किया गया नक्शा कहीं और आबादी वाला है या यदि मदद करता है तो एक अलग नक्शा कार्यान्वयन का उपयोग करता है। – blurredd

+0

ठीक है, आपकी टिप्पणी के बाद मुझे वही जवाब मिला जो कुछ मिनट पहले हुप्जे द्वारा दिया गया था, मैंने पहले जेएलएस के उस हिस्से को नजरअंदाज कर दिया था। वैसे भी, यह 5.5.2 के आधार पर एक अनचेक कास्ट है। हालांकि मुझे अभी भी लगता है कि इस तरह के कास्टों का उपयोग करना एक अच्छा अभ्यास नहीं है। – pifta

3

बेशक, जावा भाषा विशिष्टता के संदर्भ में मनाए गए व्यवहार को समझाते हुए एक उत्तर प्रदान करना संभव नहीं है जो "अधिक सही" (सही है?) प्रदान करना संभव नहीं है। हालांकि:

  • व्यावहारिक दृष्टि से देखते हुए मनाया व्यवहार की एक व्याख्या का पालन करने के लिए आसान और आसान एक स्पष्टीकरण जो केवल आपके पर JLS फेंकता से याद करने के लिए हो सकता है। नतीजतन, जेएलएस के संदर्भ में एक स्पष्टीकरण की तुलना में व्यावहारिक स्पष्टीकरण अक्सर अधिक उपयोग योग्य होता है।

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

तो, क्या हो रहा है इसके बारे में एक व्यावहारिक स्पष्टीकरण।


निम्नलिखित: क्योंकि आप एक सामान्य प्रकार के कास्टिंग नहीं कर रहे हैं

Map<?, ?> map1 = ...; 
StringToIntegerMap stringMap1 = (StringToIntegerMap)map1; 

कोई अनियंत्रित डाली चेतावनी देता है। यह निम्न कार्य रूप में ही है:

Map map4 = ...; //gives warning "raw use of generic type"; bear with me. 
StringToIntegerMap stringMap4 = (StringToIntegerMap)map4; //no unchecked warning! 

निम्नलिखित:

Map<String, Integer> map2 = ...; 
StringMap<Integer> stringMap2 = (StringMap<Integer>)map2; 

कोई अनियंत्रित डाली चेतावनी देता है क्योंकि दाहिने हाथ की ओर से बाएं हाथ की ओर मैच सामान्य बहस के सामान्य तर्क। (दोनों <String,Integer> हैं)


निम्नलिखित:

Map<?, Integer> map3 = ...; 
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3; 

एक अनियंत्रित डाली चेतावनी दे करता है क्योंकि बाएं हाथ की ओर <String,Integer> है, लेकिन दाहिने हाथ की ओर <?,Integer> है और आप हमेशा के लिए जब आप इस तरह के एक चेतावनी उम्मीद कर सकते हैं विशिष्ट प्रकार के लिए वाइल्डकार्ड डालें। (या एक बाध्य प्रकार को अधिक कड़ाई से बाध्य, अधिक विशिष्ट प्रकार के लिए।) ध्यान दें कि इस मामले में "पूर्णांक" एक लाल हेरिंग है, आपको Map<?,?> map3 = new HashMap<>();

+0

लेकिन कास्ट वास्तव में 'मानचित्र ' से 'मानचित्र <स्ट्रिंग, इंटीजर>' (जो सबसे अधिक उत्तर पता) से नहीं है, लेकिन 'मानचित्र ' से 'स्ट्रिंग मैप ' पर नहीं है। इससे सवाल अधिक दिलचस्प हो जाता है, क्योंकि यदि कोई ऑब्जेक्ट 'स्ट्रिंग मैप' (एक चेक जो रनटाइम पर किया जा सकता है) है, तो यह एक 'मानचित्र ' होना चाहिए। तो, सहजता से, कास्ट बिल्कुल अनचेक नहीं है। – Hoopje

संबंधित मुद्दे