मैं अनियंत्रित कास्ट चेतावनियों को कैसे संबोधित करूं?


610

ग्रहण मुझे निम्नलिखित रूप की चेतावनी दे रहा है:

प्रकार की सुरक्षा: ऑब्जेक्ट से हैशमैप तक अनियंत्रित कास्ट

यह एक कॉल से एक एपीआई के लिए है जिस पर मेरा कोई नियंत्रण नहीं है जो वस्तु लौटाता है:

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
  HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
  return theHash;
}

यदि संभव हो तो मैं ग्रहण की चेतावनी से बचना चाहूंगा, क्योंकि सैद्धांतिक रूप से वे कम से कम संभावित कोड समस्या का संकेत देते हैं। मैं इसे एक को खत्म करने का एक अच्छा तरीका नहीं मिला है, हालांकि। मैं एक विधि से जुड़ी एकल लाइन को अपने आप निकाल सकता हूं और @SuppressWarnings("unchecked")उस विधि में जोड़ सकता हूं , इस प्रकार कोड के ब्लॉक होने के प्रभाव को सीमित करता हूं जहां मैं चेतावनियों को नजरअंदाज करता हूं। कोई बेहतर विकल्प? मैं ग्रहण में इन चेतावनियों को बंद नहीं करना चाहता।

इससे पहले कि मैं कोड में आता, यह सरल था, लेकिन फिर भी उकसाया चेतावनी:

HashMap getItems(javax.servlet.http.HttpSession session) {
  HashMap theHash = (HashMap)session.getAttribute("attributeKey");
  return theHash;
}

जब आप हैश का उपयोग करने की कोशिश करते हैं तो समस्या कहीं और थी: आपको चेतावनी मिलेगी:

HashMap items = getItems(session);
items.put("this", "that");

Type safety: The method put(Object, Object) belongs to the raw type HashMap.  References to generic type HashMap<K,V> should be parameterized.

: आप ऐसा HttpSession उपयोग कर रहे हैं, इस विषय पर ब्रायन गोएज़ के लेख की जाँच ibm.com/developerworks/library/j-jtp09238.html
tackline - टॉम Hawtin

यदि एक अनियंत्रित डाली अपरिहार्य है, तो एक अच्छा विचार यह है कि इसे किसी ऐसी चीज से कसकर जोड़ा जाए जो तार्किक रूप से इसका प्रकार ( enumया जैसे उदाहरण Class<T>) का प्रतिनिधित्व करती है , इसलिए आप इसे देख सकते हैं और जान सकते हैं कि यह सुरक्षित है।
फिलिप जू जूल

4
संबंधित /
दुपट्टा


मैं जोड़ूंगा, मैंने पाया कि मैं केवल विधि के स्तर पर @SuppressWarnings ("अनियंत्रित") जोड़ सकता हूं जिसमें आपत्तिजनक कोड है। इसलिए मैंने एक रूटीन के लिए कोड को तोड़ दिया जहां मुझे ऐसा करना पड़ा। मैंने हमेशा सोचा था कि आप प्रश्न में रेखा के ठीक ऊपर ऐसा कर सकते हैं।
JGFMK

जवाबों:


557

जाहिर है, निश्चित रूप से अनियंत्रित कलाकारों को नहीं करना है।

यदि यह बिल्कुल आवश्यक है, तो कम से कम @SuppressWarningsएनोटेशन के दायरे को सीमित करने का प्रयास करें । अपने Javadocs के अनुसार , यह स्थानीय चर पर जा सकता है; इस तरह, यह पूरी विधि को भी प्रभावित नहीं करता है।

उदाहरण:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

यह निर्धारित करने का कोई तरीका नहीं है कि क्या Mapवास्तव में सामान्य पैरामीटर होना चाहिए <String, String>। आपको पहले से पता होना चाहिए कि पैरामीटर क्या होना चाहिए (या जब आपको पता चलेगा ClassCastException)। यही कारण है कि कोड एक चेतावनी उत्पन्न करता है, क्योंकि संकलक संभवतः यह नहीं जान सकता है कि क्या सुरक्षित है।


112
+1 यह इंगित करने के लिए कि यह स्थानीय चर पर जा सकता है। ग्रहण केवल इसे पूरी विधि में जोड़ने की पेशकश करता है ...
thSoft

17
ग्रहण 3.7 (इंडिगो) में स्थानीय चरों में अनियंत्रित जोड़ने के लिए समर्थन है।
श्वेता

78
चेतावनी सिर्फ इसलिए नहीं है कि कंपाइलर को पता नहीं है कि कास्ट सुरक्षित है। उदाहरण के लिए String s = (String) new Object() ;, कोई चेतावनी नहीं मिलती है, भले ही संकलक को पता न हो कि कास्ट सुरक्षित है। चेतावनी इसलिए है क्योंकि संकलक (ए) को पता नहीं है कि कास्ट सुरक्षित है और (बी) कलाकारों के बिंदु पर एक पूर्ण रन-टाइम चेक उत्पन्न नहीं करेगा। एक चेक होगा कि यह एक है Hashmap, लेकिन एक चेक नहीं होगा कि यह एक है HashMap<String,String>
थियोडोर नॉरवेल

9
अफसोस की बात है, भले ही कास्ट और चेतावनी असाइनमेंट के लिए हो , एनोटेशन को वेरिएबल डिक्लेरेशन पर जाना होता है ... इसलिए यदि डिक्लेरेशन और असाइनमेंट अलग-अलग जगहों पर हैं (क्रमशः, 'try' ब्लॉक के बाहर और अंदर कहते हैं) , ग्रहण अब दो चेतावनियाँ उत्पन्न करता है: मूल अनियंत्रित डाली, और एक नया "अनावश्यक एनोटेशन" नैदानिक।
टीआई स्ट्रगा जूल

6
स्थानीय चर घोषणा के साथ आने वाले एनोटेशन के लिए एक वर्कअराउंड, जो वास्तविक कलाकारों की तुलना में एक अलग लाइन पर एक अलग दायरे में हो सकता है, विशेष रूप से एक ही लाइन पर कलाकारों का प्रदर्शन करने के लिए विशेष रूप से कलाकारों के दायरे में एक स्थानीय चर बनाने के लिए है। घोषणा के रूप में। फिर इस वेरिएबल को वास्तविक वेरिएबल में असाइन करें जो एक अलग दायरे में है। यह वह विधि है जिसका उपयोग मैंने एक कास्ट पर एक चेतावनी चर पर चेतावनी को दबाने के लिए भी किया था क्योंकि एनोटेशन को यहां भी लागू नहीं किया जा सकता है।
जेफ़ लॉकहार्ट

167

दुर्भाग्य से, यहाँ कोई बढ़िया विकल्प नहीं हैं। याद रखें, इन सभी का लक्ष्य प्रकार की सुरक्षा को संरक्षित करना है। " जावा जेनेरिक " गैर-जेनेरिक लीगेसी लाइब्रेरी से निपटने के लिए एक समाधान प्रस्तुत करता है, और विशेष रूप से एक है जिसे खंड 8.2 में "खाली लूप तकनीक" कहा जाता है। मूल रूप से, असुरक्षित कास्ट करें, और चेतावनी को दबाएं। फिर इस तरह से नक्शे के माध्यम से पाश:

@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());

यदि कोई अप्रत्याशित प्रकार सामने आया है, तो आपको एक रनटाइम मिलेगा ClassCastException, लेकिन कम से कम यह समस्या के स्रोत के करीब होगा।


6
बहुत, कई कारणों से स्किपहॉपी द्वारा प्रदान किए गए एक से बेहतर जवाब: 1) यह कोड बहुत अधिक है, बहुत कम है। 2) यह कोड वास्तव में ClassCastException को अपेक्षित रूप से फेंकता है। 3) यह कोड सोर्स मैप की पूरी कॉपी नहीं करता है। 4) छोरों को एक अलग तरीके से आसानी से लपेटा जा सकता है जो एक मुखर में उपयोग किया जाता है, जो उत्पादन कोड में प्रदर्शन हिट को आसानी से हटा देगा।
स्टिजन डे विट

6
क्या कोई संभावना नहीं है कि जावा कंपाइलर या जेआईटी कंपाइलर यह तय करेगा कि इस कोड के परिणामों का उपयोग नहीं किया जा रहा है और इसे संकलित नहीं करके "अनुकूलित करें"?
RenniePet

1
यदि यह संभावित रूप से अपवाद को फेंक सकता है तो यह वास्तव में मृत कोड नहीं है। मुझे आज जेआईटी कंपाइलरों के बारे में इतना पता नहीं है कि यह गारंटी देने के लिए कि उनमें से कोई भी इसे गड़बड़ नहीं करेगा, लेकिन मैं यह कहने में काफी आत्मविश्वास महसूस करता हूं कि वे नहीं चाहते हैं।
ग्रैंडऑनर

3
यह अभी भी प्रकार की सुरक्षा की गारंटी नहीं देता है क्योंकि उसी मानचित्र का उपयोग अभी भी किया जा रहा है। यह मूल रूप से मैप <ऑब्जेक्ट, ऑब्जेक्ट> के रूप में परिभाषित किया गया है कि बस में स्ट्रिंग्स और नंबर होते हैं और फिर बाद में अगर एक बूलियन जोड़ा जाता है तो इस कोड के उपयोगकर्ता को आश्चर्यचकित करने के लिए एक भ्रामक और कठिन हो जाएगा। प्रकार की सुरक्षा की गारंटी देने का एकमात्र तरीका यह है कि अनुरोधित प्रकार के साथ एक नए नक्शे में इसे कॉपी करें जो गारंटी देता है कि इसमें जाने की क्या अनुमति है।
user2219808

112

वाह; मुझे लगता है कि मुझे अपने ही सवाल का जवाब पता चल गया है। मुझे यकीन नहीं है कि यह इसके लायक है! :)

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

लेकिन, आप हैश में प्रत्येक वस्तु पर एक चेक प्रदर्शन कर सकते हैं, उदाहरण के लिए, और ऐसा करने पर, आप एक नए हैश का निर्माण कर सकते हैं जो टाइप-सेफ है। और आप किसी भी चेतावनी को भड़काने नहीं देंगे।

Mmyers और Esko Luontola के लिए धन्यवाद, मैंने कोड को मूल रूप से यहाँ लिखा है, इसलिए मैंने इसे एक उपयोगिता वर्ग में लपेटा है और इसे किसी भी मानकीकृत HashMap के लिए उपयोग किया जा सकता है। यदि आप इसे बेहतर ढंग से समझना चाहते हैं और जेनेरिक से बहुत परिचित नहीं हैं, तो मैं इस उत्तर के संपादित इतिहास को देखने के लिए प्रोत्साहित करता हूं।

public static <K, V> HashMap<K, V> castHash(HashMap input,
                                            Class<K> keyClass,
                                            Class<V> valueClass) {
  HashMap<K, V> output = new HashMap<K, V>();
  if (input == null)
      return output;
  for (Object key: input.keySet().toArray()) {
    if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
        Object value = input.get(key);
        if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
            K k = keyClass.cast(key);
            V v = valueClass.cast(value);
            output.put(k, v);
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", value "+ value +" is not a "+ valueClass.getSimpleName()
            );
        }
    } else {
        throw new AssertionError(
            "Cannot cast to HashMap<"+ keyClass.getSimpleName()
            +", "+ valueClass.getSimpleName() +">"
            +", key "+ key +" is not a " + keyClass.getSimpleName()
        );
    }
  }
  return output;
}

यह बहुत काम है, संभवतः बहुत कम इनाम के लिए ... मुझे यकीन नहीं है कि मैं इसका उपयोग करूंगा या नहीं। मैं किसी भी टिप्पणी की सराहना करता हूं कि क्या लोगों को लगता है कि यह इसके लायक है या नहीं। इसके अलावा, मैं सुधार सुझावों की सराहना करता हूं: क्या कुछ बेहतर है जो मैं जोर देने के अलावा कर सकता हूं? वहाँ कुछ बेहतर मैं फेंक सकता है? क्या मुझे इसे एक जाँच अपवाद बनाना चाहिए?


68
यह सामान भ्रामक है, लेकिन मुझे लगता है कि आपने जो कुछ भी किया है, वह क्लासरूम अपवादों के लिए जोर-शोर से किया गया है।
डस्टिन गेट्ज़

59
यार, यह निश्चित रूप से इसके लायक नहीं है! उस गरीब के बारे में कल्पना करें जिसे वापस आना है और उस गड़बड़ में कुछ कोड को संशोधित करना है। मुझे चेतावनी को दबाना पसंद नहीं है, लेकिन मुझे लगता है कि यहाँ कम बुराई है।
क्रेग बी

69
यह सिर्फ इतना ही नहीं है कि यह एक बदसूरत, भ्रामक, गड़बड़ है (जब आप एक प्रचुर टिप्पणी से बच नहीं सकते हैं तो इसके माध्यम से रखरखाव प्रोग्रामर चल सकते हैं); संग्रह के प्रत्येक तत्व पर पुनरावृत्ति करना कलाकारों को O (1) से O (n) ऑपरेशन में बदल देता है। यह एक ऐसी चीज है जिसकी कभी उम्मीद नहीं की जाएगी और आसानी से एक भयानक रहस्य मंदी में बदल सकती है।
दान

22
@DanNeely आप सही हैं। सामान्य तौर पर, किसी को भी कभी भी ऐसा नहीं करना चाहिए।
स्किप्पॉपी

4
कुछ टिप्पणियां ... विधि हस्ताक्षर गलत है क्योंकि यह एक लानत की बात नहीं करता है, यह सिर्फ मौजूदा नक्शे को एक नए नक्शे में कॉपी करता है। इसके अलावा, यह संभवत: किसी भी मानचित्र को स्वीकार करने के लिए रिफैक्ट किया जा सकता है, और स्वयं ही हाशप पर भरोसा नहीं करता है (अर्थात विधि हस्ताक्षर में मानचित्र को वापस ले लें, भले ही आंतरिक प्रकार हाशप हो)। आपको वास्तव में ढलाई या भंडारण को एक नए मानचित्र में करने की आवश्यकता नहीं है - यदि आप एक जोरदार त्रुटि नहीं फेंकते हैं, तो दिए गए नक्शे में अभी इसके अंदर सही प्रकार हैं। सामान्य प्रकार के साथ एक नया नक्शा बनाना व्यर्थ है क्योंकि आप अभी भी इसे कच्चा बना सकते हैं और जो कुछ भी डाल सकते हैं।
MetroidFan2002

51

ग्रहण की वरीयताओं में, जावा-> कंपाइलर-> त्रुटियों / चेतावनियों-> सामान्य प्रकारों पर जाएं और जांचें Ignore unavoidable generic type problems चेक-बॉक्स जांच करें।

यह प्रश्न के इरादे को संतुष्ट करता है, अर्थात

मैं ग्रहण की चेतावनी से बचना चाहूंगा ...

यदि आत्मा नहीं है।


1
आह, इसके लिए धन्यवाद :) मुझे इसमें " uses unchecked or unsafe operations." त्रुटि मिल रही थी javac, लेकिन @SuppressWarnings("unchecked")ग्रहण को दुखी बनाकर दावा किया कि दमन अनावश्यक था। इस बॉक्स को अनचेक करने से एक्लिप्स बनता है और javacवही व्यवहार होता है, जो मुझे चाहिए था। स्पष्ट रूप से कोड में चेतावनी को दबाने से ग्रहण के अंदर हर जगह इसे दबाने की तुलना में बहुत स्पष्ट है।
dimo414

26

आप निम्न की तरह एक उपयोगिता वर्ग बना सकते हैं, और इसका उपयोग अनियंत्रित चेतावनी को दबाने के लिए कर सकते हैं।

public class Objects {

    /**
     * Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }
}

आप इसका उपयोग इस प्रकार कर सकते हैं:

import static Objects.uncheckedCast;
...

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
      return uncheckedCast(session.getAttribute("attributeKey"));
}

इसके बारे में कुछ और चर्चा यहाँ है: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html


18
डाउनवोटिंग नहीं, लेकिन रैपर सिर्फ चेतावनी को दबाने पर कुछ भी नहीं जोड़ता है।
डस्टिन गेट्ज़

3
+1 के रूप में यह समाधान कीमती कोड लाइनों को बर्बाद नहीं करता है।
टिनो

1
@ बहुत ज्यादा। बहुत अधिक महंगा बड़ा और उच्च-रिज़ॉल्यूशन मॉनिटर उन सभी बर्बाद लाइनों को कमरा देने के लिए, एक बड़ा डेस्क उन सभी बड़े मॉनिटरों पर लगाने के लिए, एक बड़ा कमरा बड़ा डेस्क लगाने के लिए और एक व्यावहारिक बॉस ..
टीनो

1
@ ErikE स्क्रॉलबार, के लिए vi? क्या तुम मजाक कर रहे हो?
टीनो

21

यह सामान कठिन है, लेकिन यहाँ मेरे वर्तमान विचार हैं:

यदि आपका एपीआई ऑब्जेक्ट लौटाता है, तो ऐसा कुछ भी नहीं है जो आप कर सकते हैं - कोई बात नहीं, आप नेत्रहीन रूप से ऑब्जेक्ट को कास्टिंग करेंगे। आप जावा को ClassCastException को फेंकने देते हैं, या आप प्रत्येक तत्व को स्वयं जांच सकते हैं और जोर लगा सकते हैं या IllegalArgumentException या कुछ ऐसे, लेकिन ये रनटाइम चेक सभी समकक्ष हैं। आपको संकलित समय को अनचेक करना होगा, चाहे आप रनटाइम में क्या करें।

मैं सिर्फ कास्ट ब्लाइंड करना पसंद करूंगा और JVM को मेरे लिए रनटाइम चेक करने देना चाहिए क्योंकि हम "जानते हैं" कि एपीआई को क्या लौटना चाहिए, और आमतौर पर यह मानने को तैयार रहते हैं कि एपीआई काम करता है। कास्ट के ऊपर हर जगह जेनेरिक का उपयोग करें, यदि आपको उनकी आवश्यकता है। आप वास्तव में कुछ भी नहीं खरीद रहे हैं क्योंकि आपके पास अभी भी सिंगल ब्लाइंड कास्ट है, लेकिन कम से कम आप वहां से जेनेरिक का उपयोग कर सकते हैं इसलिए जेवीएम आपके कोड के अन्य टुकड़ों में नेत्रहीन कास्ट से बचने में आपकी मदद कर सकता है।

इस विशेष मामले में, संभवतः आप SetAttribute को कॉल देख सकते हैं और देख सकते हैं कि प्रकार अंदर जा रहा है, इसलिए केवल अंधा-कास्टिंग को उसी तरह से बाहर करना अनैतिक नहीं है। एक टिप्पणी जोड़ें जिसमें SetAttribute का उल्लेख किया गया हो और इसके साथ किया जाए।


16

यहां एक छोटा उदाहरण है जो अन्य उत्तरों में उल्लिखित दो रणनीतियों को नियोजित करके "अनियंत्रित कलाकारों" की चेतावनी से बचा जाता है।

  1. रनटाइम पर एक पैरामीटर के रूप में ब्याज के प्रकार के वर्ग को पास करें ( Class<T> inputElementClazz)। तो आप का उपयोग कर सकते हैं:inputElementClazz.cast(anyObject);

  2. एक संग्रह के प्रकार के कास्टिंग के लिए, वाइल्डकार्ड का उपयोग करें? एक सामान्य प्रकार टी के बजाय यह स्वीकार करने के लिए कि आप वास्तव में विरासत कोड ( Collection<?> unknownTypeCollection) से किस तरह की वस्तुओं की उम्मीद नहीं करते हैं । आखिरकार, यह "अनियंत्रित डाली" चेतावनी है जो हमें बताना चाहती है: हम यह सुनिश्चित नहीं कर सकते हैं कि हमें एक मिल Collection<T>जाए, इसलिए ऐसा करने के लिए ईमानदार बात है Collection<?>। यदि बिल्कुल आवश्यक हो, तो एक ज्ञात प्रकार का संग्रह अभी भी बनाया जा सकता है ( Collection<T> knownTypeCollection)।

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

public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
    IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
    // legacy code returns an Object from getFirstElement,
    // the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
    T firstElement = inputElementClazz.cast(selection.getFirstElement());

    // legacy code returns an object from getInput, so we deal with it as a Collection<?>
    Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();

    // for some operations we do not even need a collection with known types
    unknownTypeCollection.remove(firstElement);

    // nothing prevents us from building a Collection of a known type, should we really need one
    Collection<T> knownTypeCollection = new ArrayList<T>();
    for (Object object : unknownTypeCollection) {
        T aT = inputElementClazz.cast(object);
        knownTypeCollection.add(aT);
        System.out.println(aT.getClass());
    }

    structuredViewer.refresh();
}

स्वाभाविक रूप से, उपरोक्त कोड रनटाइम त्रुटियां दे सकता है यदि हम गलत डेटा प्रकारों के साथ विरासत कोड का उपयोग करते हैं (उदाहरण के लिए, यदि हम जावा संग्रह के बजाय स्ट्रक्चर्ड व्यूअर के "इनपुट" के रूप में एक सरणी सेट करते हैं)।

विधि को कॉल करने का उदाहरण:

dragFinishedStrategy.dragFinished(viewer, Product.class);

13

HTTP सत्र दुनिया में आप वास्तव में कलाकारों से बच नहीं सकते हैं, क्योंकि एपीआई को इस तरह लिखा जाता है (केवल लेता है और वापस लौटता है Object )।

थोड़े से काम से आप अनियंत्रित कलाकारों से आसानी से बच सकते हैं, 'हालांकि। इसका मतलब है कि यह एक पारंपरिक कलाकार में बदल जाएगा ClassCastExceptionजहां एक त्रुटि की स्थिति में एक अधिकार है)। एक अनियंत्रित अपवाद एक में बदल सकता हैCCE किसी भी बिंदु पर बाद में कास्ट के बिंदु पर (यही कारण है कि यह एक अलग चेतावनी है)।

एक समर्पित वर्ग के साथ हाशप को बदलें:

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Attributes extends AbstractMap<String, String> {
    final Map<String, String> content = new HashMap<String, String>();

    @Override
    public Set<Map.Entry<String, String>> entrySet() {
        return content.entrySet();
    }

    @Override
    public Set<String> keySet() {
        return content.keySet();
    }

    @Override
    public Collection<String> values() {
        return content.values();
    }

    @Override
    public String put(final String key, final String value) {
        return content.put(key, value);
    }
}

इसके बजाय उस वर्ग में जाएं Map<String,String>और सब कुछ ठीक उसी स्थान पर जांचा जाएगा जहां आप अपना कोड लिखते हैं। ClassCastExceptionsबाद में कोई अप्रत्याशित नहीं ।


यह वास्तव में उपयोगी उत्तर है।
GPrathap

10

यदि आप निरीक्षण का उपयोग करना चाहते हैं, तो Android Studio में:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

2
यह इंटेलीज आईडीई में भी काम करता है
नेक्सस

8

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

लेकिन अगर आप किसी भी तरह से यह जांचना चाहते हैं कि मानचित्र की सामग्री सही प्रकार की है, तो आप इस तरह एक कोड का उपयोग कर सकते हैं:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("a", 1);
    map.put("b", 2);
    Object obj = map;

    Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
    Map<String, String> error = safeCastMap(obj, String.class, String.class);
}

@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
    checkMap(map);
    checkMapContents(keyType, valueType, (Map<?, ?>) map);
    return (Map<K, V>) map;
}

private static void checkMap(Object map) {
    checkType(Map.class, map);
}

private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        checkType(keyType, entry.getKey());
        checkType(valueType, entry.getValue());
    }
}

private static <K> void checkType(Class<K> expectedType, Object obj) {
    if (!expectedType.isInstance(obj)) {
        throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
    }
}

1
बहुत बढ़िया; मुझे लगता है कि मैं इसे संयोजित करने के लिए अपने जवाब के साथ जोड़ सकता हूं और चेतावनी को पूरी तरह से दबाने से बच सकता हूं!
स्किप्पॉपी

1
+1 शायद सबसे अच्छा नुस्खा (समझने और बनाए रखने में आसान) इसे रनटाइम चेक के साथ सुरक्षित रूप से करने के लिए
टिनो

8

Esko Luontola द्वारा दिए गए उत्तर में ObjectUUchecked उपयोगिता फ़ंक्शन प्रोग्राम अव्यवस्था से बचने का एक शानदार तरीका है।

यदि आप संपूर्ण विधि पर SuppressWarnings नहीं चाहते हैं, तो Java आपको इसे स्थानीय पर डालने के लिए बाध्य करता है। यदि आपको किसी सदस्य पर एक कास्ट की आवश्यकता है तो यह इस तरह कोड को जन्म दे सकता है:

@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;

उपयोगिता का उपयोग करना बहुत साफ है, और यह अभी भी स्पष्ट है कि आप क्या कर रहे हैं:

this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());

नोट: मुझे यह जोड़ना महत्वपूर्ण लगता है कि कभी-कभी चेतावनी का अर्थ है कि आप कुछ गलत कर रहे हैं जैसे:

ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList; 

 // this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList  = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here

कंपाइलर आपको जो बता रहा है, वह यह है कि इस कास्ट को रनटाइम पर चेक नहीं किया जाएगा, इसलिए जब तक आप जेनरिक कंटेनर में डेटा एक्सेस करने की कोशिश नहीं करेंगे, तब तक कोई रनटाइम एरर नहीं उठाया जाएगा।


5

चेतावनी दमन एक समाधान नहीं है। आपको एक स्टेटमेंट में दो लेवल की कास्टिंग नहीं करनी चाहिए।

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {

    // first, cast the returned Object to generic HashMap<?,?>
    HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");

    // next, cast every entry of the HashMap to the required type <String, String>
    HashMap<String, String> returingHash = new HashMap<>();
    for (Entry<?, ?> entry : theHash.entrySet()) {
        returingHash.put((String) entry.getKey(), (String) entry.getValue());
    }
    return returingHash;
}

1
उनका पांच साल पुराना सवाल? क्या आपको इतना काम करने की ज़रूरत है? यह देखते हुए कि जावा में दूसरी प्रकार की हैशमप है जो रनटाइम के पहले के समान होनी चाहिए; मुझे लगता है कि यह अधिक कुशल होगा और कॉपी से बचना होगा यदि आप सिर्फ प्रविष्टियों के माध्यम से पुनरावृत्ति करते हैं और सत्यापित करते हैं कि वे सभी तार के उदाहरण थे। या, TBH, आपके द्वारा उपयोग किए जा रहे सर्वलेट JAR के स्रोत का निरीक्षण करें और सत्यापित करें कि यह केवल तार लगाता है।
Rup

1
आज तक मैं इस चेतावनी को परियोजनाओं में देख रहा हूं। उनकी समस्या के प्रकार का सत्यापन नहीं था, लेकिन एक "मानचित्र" के कारण एक अनधिकृत मानचित्र में एक चेतावनी।
अब्बास

2

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

HashMap<String, Object> test = new HashMap();

जब आपको करने की आवश्यकता होगी तो चेतावनी का उत्पादन करेगा

HashMap<String, Object> test = new HashMap<String, Object>();

यह देखने लायक हो सकता है

जावा प्रोग्रामिंग लैंग्वेज में जेनरिक

अगर आपके अपरिचित को क्या करने की जरूरत है।


1
दुर्भाग्य से यह एक आसान स्थिति नहीं है। कोड जोड़ा गया।
स्किपहोप्पी

1
मैं यहां थोड़ी अलग समस्या का जवाब ढूंढने आया था: और आपने मुझे वही बताया जो मुझे चाहिए था! धन्यवाद!
स्टेटिक्सन

2

मुझे यह प्रश्न गलत लग सकता है (एक उदाहरण और आसपास की कुछ पंक्तियाँ अच्छी होंगी), लेकिन आप हमेशा एक उपयुक्त इंटरफ़ेस (और Java5 +) का उपयोग क्यों नहीं करते? मुझे कोई कारण नहीं है कि आप कभी एक के HashMapबजाय कास्ट करना चाहते हैं Map<KeyType,ValueType>। वास्तव में, मैं इसके बजाय एक चर के प्रकार को निर्धारित करने के लिए किसी भी कारण की कल्पना नहीं कर सकता ।HashMapMap

और स्रोत क्यों है Object? क्या यह विरासत संग्रह का एक पैरामीटर प्रकार है? यदि ऐसा है, तो जेनेरिक का उपयोग करें और इच्छित प्रकार निर्दिष्ट करें।


2
मुझे पूरा यकीन है कि इस मामले में मैप पर स्विच करने से कुछ भी नहीं बदलेगा, लेकिन प्रोग्रामिंग टिप के लिए धन्यवाद, जो बेहतर होने के लिए कुछ चीजें करने का तरीका बदल सकता है। ऑब्जेक्ट का स्रोत एक एपीआई है जिस पर मेरा कोई नियंत्रण नहीं है (कोड जोड़ा गया)।
स्किपहोप्पी

2

अगर मुझे एक एपीआई का उपयोग करना है जो जेनरिक का समर्थन नहीं करता है .. तो मैं कोशिश करता हूं कि उन कॉल को रैपर रूटीन में अलग-अलग लाइनों के साथ अलग कर दूं। मैं तब सप्रेसवर्निंग्स एनोटेशन का उपयोग करता हूं और उसी समय टाइप-सेफ्टी कास्ट भी जोड़ता हूं।

चीजों को साफ-सुथरा रखने के लिए यह केवल एक व्यक्तिगत प्राथमिकता है।


2

इसे ले लो, यह एक नया हैशपॉप बनाने की तुलना में बहुत तेज़ है, अगर यह पहले से ही एक है, लेकिन फिर भी सुरक्षित है, क्योंकि प्रत्येक तत्व की जाँच की जाती है, जैसे कि यह ...

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }

key.isAssignableFrom(e.getKey().getClass())के रूप में लिखा जा सकता हैkey.isInstance(e.getKey())
user102008

1

आप इसे डालने से पहले बस इसे टाइप करें।

Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;  

और किसी से भी पूछने के लिए, यह उन वस्तुओं को प्राप्त करने के लिए काफी सामान्य है जहां आप प्रकार के बारे में सुनिश्चित नहीं हैं। विरासत "SOA" के बहुत सारे कार्यान्वयन विभिन्न वस्तुओं के आसपास से गुजरते हैं जिन्हें आपको हमेशा भरोसा नहीं करना चाहिए। (भयावहता!)

EDIT ने पोस्टर के अपडेट से मिलान करने के लिए एक बार उदाहरण कोड को बदल दिया, और कुछ टिप्पणियों के बाद मुझे लगता है कि इंस्टाफॉनिक जेनरिक के साथ अच्छी तरह से नहीं खेलता है। हालाँकि बाहरी वस्तु को मान्य करने के लिए चेक को बदलना कमांडलाइन कंपाइलर के साथ अच्छा लगता है। अब संशोधित उदाहरण पोस्ट किया गया।


8
दुर्भाग्य से, जेनरिक उस असंभव को प्रस्तुत करते हैं। यह न केवल एक HashMap है, यह प्रकार जानकारी के साथ एक HashMap है। और अगर मैं उस जानकारी को समाप्त कर दूं, तो मैं चेतावनी को कहीं और धकेल दूंगा।
स्किपहोप्पी

1

कंप्यूटर साइंस में लगभग हर समस्या को इनडायरेक्शन *, या कुछ के स्तर से जोड़कर हल किया जा सकता है।

तो एक गैर-जेनेरिक वस्तु का परिचय दें जो उच्च स्तर का है जो कि ए Map। बिना किसी संदर्भ के यह बहुत आश्वस्त करने वाला नहीं है, लेकिन फिर भी:

public final class Items implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Map<String,String> map;
    public Items(Map<String,String> map) {
        this.map = New.immutableMap(map);
    }
    public Map<String,String> getMap() {
        return map;
    }
    @Override public String toString() {
        return map.toString();
    }
}

public final class New {
    public static <K,V> Map<K,V> immutableMap(
        Map<? extends K, ? extends V> original
    ) {
        // ... optimise as you wish...
        return Collections.unmodifiableMap(
            new HashMap<String,String>(original)
        );
    }
}

static Map<String, String> getItems(HttpSession session) {
    Items items = (Items)
        session.getAttribute("attributeKey");
    return items.getMap();
}

* अप्रत्यक्ष के कई स्तरों को छोड़कर।


1
उद्धरण का श्रेय दिवंगत प्रोफेसर डेविड व्हीलर को दिया जाता है। en.wikipedia.org/wiki/…
स्टीफन C

1

जब मैं equals()ऑपरेशन को ओवरराइड करता हूं, तो एक तरीका यह है ।

public abstract class Section<T extends Section> extends Element<Section<T>> {
    Object attr1;

    /**
    * Compare one section object to another.
    *
    * @param obj the object being compared with this section object
    * @return true if this section and the other section are of the same
    * sub-class of section and their component fields are the same, false
    * otherwise
    */       
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            // this exists, but obj doesn't, so they can't be equal!
            return false;
        }

        // prepare to cast...
        Section<?> other;

        if (getClass() != obj.getClass()) {
            // looks like we're comparing apples to oranges
            return false;
        } else {
            // it must be safe to make that cast!
            other = (Section<?>) obj;
        }

        // and then I compare attributes between this and other
        return this.attr1.equals(other.attr1);
    }
}

यह जावा 8 में काम करने लगता है (यहां तक ​​कि इसके साथ संकलित -Xlint:unchecked)


0

यदि आप सुनिश्चित हैं कि प्रकार session.getAttribute () द्वारा लौटाया गया है तो HashMap है, तो आप उस सटीक प्रकार को टाइपकास्ट नहीं कर सकते हैं, लेकिन केवल सामान्य HashMap की जाँच करने पर निर्भर हैं

HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
    HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
    return theHash;
} 

ग्रहण तब चेतावनी को आश्चर्यचकित करेगा, लेकिन निश्चित रूप से यह रनटाइम त्रुटियों को जन्म दे सकता है जो डिबग करना कठिन हो सकता है। मैं इस दृष्टिकोण का उपयोग केवल संचालन-महत्वपूर्ण संदर्भों में नहीं करता हूं।


0

दो तरीके, एक जो पूरी तरह से टैग से बचता है, दूसरा एक शरारती लेकिन अच्छी उपयोगिता पद्धति का उपयोग करके।
समस्या प्री-जेनेरिक कलेक्शन है ...
मेरा मानना ​​है कि अंगूठे का नियम है: "वस्तुओं को एक समय में एक कास्ट करें" - सामान्य दुनिया में कच्ची कक्षाओं का उपयोग करने की कोशिश करते समय इसका क्या मतलब है क्योंकि आपको पता नहीं है कि क्या है इस मानचित्र में है <??>? (और वास्तव में JVM को यह भी पता लग सकता है कि यह मैप भी नहीं है!), यह स्पष्ट है कि जब आप इसके बारे में सोचते हैं कि आप इसे नहीं डाल सकते। यदि आपके पास एक मानचित्र <string;> map2 है तो HashSet <String> keys = (HashSet <String>) map2.keySet () आपको चेतावनी नहीं देता है, बावजूद इसके कि यह "संकलक के लिए विश्वास का कार्य" है (क्योंकि यह ट्रीस्सेट हो सकता है) ... लेकिन यह केवल विश्वास का एक कार्य है।

पीएस पर आपत्ति जताते हुए कि मेरे पहले तरीके से "बोरिंग" और "समय लगता है" के रूप में पुनरावृत्ति होती है, इसका जवाब "नो पेन नो गेन" है: एक जेनेरिक कलेक्शन में Map.Entry <String, String> s और कुछ भी नहीं होने की गारंटी है। अन्य। आपको इस गारंटी के लिए भुगतान करना होगा। सामान्य रूप से इस भुगतान का उपयोग करते समय, खूबसूरती से, कोडिंग अनुपालन का रूप लेता है, मशीन के समय का नहीं!
एक विचारधारा कह सकती है कि आपको चेतावनियों के बजाय ऐसी अनियंत्रित जातियों की त्रुटियों को बनाने के लिए ग्रहण की सेटिंग करनी चाहिए। उस स्थिति में आपको मेरा पहला तरीका इस्तेमाल करना होगा।

package scratchpad;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

public class YellowMouse {

    // First way

    Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
      Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");

      Map<String, String> yellowMouse = new HashMap<String, String>();
      for( Map.Entry<?, ?> entry : theHash.entrySet() ){
        yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
      }

      return yellowMouse;
    }


    // Second way

    Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
      return uncheckedCast( session.getAttribute("attributeKey") );
    }


    // NB this is a utility method which should be kept in your utility library. If you do that it will
    // be the *only* time in your entire life that you will have to use this particular tag!!

    @SuppressWarnings({ "unchecked" })
    public static synchronized <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }


}

यह तथ्य कि आपके पास टिप्पणी विशेषाधिकार नहीं हैं, आप अपनी टिप्पणियों को जोड़ने के लिए दूसरों के उत्तरों को संपादित करने की अनुमति नहीं देते हैं; आप उन्हें स्वरूपण, वाक्यविन्यास, ... में सुधार करने के लिए दूसरों के उत्तरों को संपादित करते हैं, उन पर अपनी राय जोड़ने के लिए नहीं। जब आप 50 प्रतिनिधि तक पहुंच जाएंगे, तो आप हर जगह टिप्पणी कर पाएंगे, इस बीच मुझे पूरा यकीन है कि आप विरोध कर सकते हैं (या, यदि आप वास्तव में नहीं कर सकते हैं, तो अपनी टिप्पणी अपने पोस्ट में मौजूदा उत्तरों के लिए लिखें)। (दूसरों के लिए ध्यान दें: मैं इसे इसलिए लिखता हूं क्योंकि मैंने देखा - और अस्वीकार कर दिया - उनके प्रस्तावित टिप्पणियों-मॉडरेशन टूल में अन्य पदों के लिए संपादन)
मैटियो इटालिया

-1

इससे चेतावनी दूर हो जाती है ...

 static Map<String, String> getItems(HttpSession session) {
        HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
        HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
    return theHash;
}

1
नहीं, यह नहीं है। वास्तव में, यह दो चेतावनियां बनाता है जहां पहले एक था।
स्टिजन डी विट

आह अच्छा। यकीन नहीं होता कि मैंने ऐसा क्यों सोचा।
गुनगुनी

-3

समाधान: इस चेतावनी को ग्रहण में अक्षम करें। @SuppressWarnings न करें, बस इसे पूरी तरह से अक्षम करें।

ऊपर प्रस्तुत "समाधानों" में से कई लाइन से बाहर हैं, जो एक मूर्खतापूर्ण चेतावनी को दबाने के लिए कोड को अपठनीय बनाते हैं।


9
मैं पूछ सकता हूँ क्यों? विश्व स्तर पर एक चेतावनी को अक्षम करने से अन्य स्थानों को छिपाया जाएगा जहां यह मुद्दा वास्तविक है। @SuppressWarningsकोड जोड़ने से कोई भी अपठनीय नहीं बनता है।
एमबीडी २D
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.