प्रकार की सुरक्षा: अनियंत्रित डाली


265

मेरी स्प्रिंग एप्लिकेशन संदर्भ फ़ाइल में, मेरे पास कुछ इस तरह है:

<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
    <entry key="some_key" value="some value" />
    <entry key="some_key_2" value="some value" />   
</util:map>

जावा वर्ग में, कार्यान्वयन इस तरह दिखता है:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

ग्रहण में, मुझे एक चेतावनी दिखाई देती है जो कहती है:

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

मैंने गलत क्या किया? मैं समस्या को कैसे हल करूं?


मैं नियमित रूप से हाशमैप करने के लिए कलाकारों की जांच करने के लिए एक रूटीन के साथ आया था, जो अनियंत्रित कास्ट चेतावनी को समाप्त करता है: लिंक मैं कहूंगा कि यह "सही" समाधान है, लेकिन क्या इसके लायक है या नहीं यह विवादास्पद हो सकता है। :)
स्किपहोप्पी


जवाबों:


248

ठीक है, सबसे पहले, आप नए HashMapनिर्माण कॉल के साथ मेमोरी बर्बाद कर रहे हैं । आपकी दूसरी पंक्ति इस बनाए गए हैशमैप के संदर्भ को पूरी तरह से अस्वीकृत कर देती है, जिससे यह फिर कचरा कलेक्टर को उपलब्ध हो जाता है। तो, ऐसा मत करो, उपयोग करें:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

दूसरे, संकलक शिकायत कर रहा है कि आप वस्तु को HashMapबिना जाँच के डालते हैं यदि वह ए HashMap। लेकिन, भले ही आप ऐसा करें:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

आपको शायद अभी भी यह चेतावनी मिलेगी। समस्या है, getBeanरिटर्न Object, इसलिए यह अज्ञात है कि प्रकार क्या है। इसे HashMapसीधे रूप में परिवर्तित करने से दूसरे मामले में समस्या उत्पन्न नहीं होगी (और शायद पहले मामले में चेतावनी नहीं होगी, मुझे यकीन नहीं है कि जावा कंपाइलर जावा 5 के लिए चेतावनी के साथ कितना कठिन है)। हालाँकि, आप इसे एक में परिवर्तित कर रहे हैं HashMap<String, String>

HashMaps वास्तव में नक्शे हैं जो एक वस्तु को एक कुंजी के रूप में लेते हैं और एक वस्तु के रूप में एक मूल्य होता है, HashMap<Object, Object>यदि आप करेंगे। इस प्रकार, इस बात की कोई गारंटी नहीं है कि जब आप अपने बीन को प्राप्त करते हैं कि इसका प्रतिनिधित्व HashMap<String, String>इसलिए किया जा सकता है क्योंकि आपके पास हो सकता है HashMap<Date, Calendar>क्योंकि गैर-जेनेरिक प्रतिनिधित्व जो लौटाया गया है उसमें कोई वस्तु हो सकती है।

यदि कोड संकलित करता है, और आप String value = map.get("thisString");बिना किसी त्रुटि के निष्पादित कर सकते हैं, तो इस चेतावनी के बारे में चिंता न करें। लेकिन अगर नक्शा स्ट्रिंग मानों के लिए पूरी तरह से स्ट्रिंग कुंजियों का नहीं है, तो आपको एक ClassCastExceptionरनटाइम मिलेगा , क्योंकि जेनरिक इस मामले में होने से रोक नहीं सकते हैं।


12
यह कुछ समय पहले था, लेकिन मैं एक कलाकारों से पहले सेट <CustomClass> की जाँच करने के प्रकार पर एक उत्तर की तलाश में था, और आप एक पैरामीरिज्ड जेनेरिक पर उदाहरण नहीं दे सकते। जैसे अगर (event.getTarget instof सेट करें <CustomClass>) आप केवल एक के साथ एक जेनेरिक की जाँच कर सकते हैं? और यह कास्ट चेतावनी को दूर नहीं करेगा। जैसे अगर (event.getTarget instof सेट <?>)
लहसुन

315

समस्या यह है कि एक डाली एक क्रम की जांच है - लेकिन प्रकार विलोपन के कारण, कार्यावधि में वहाँ वास्तव में एक के बीच कोई अंतर नहीं है HashMap<String,String>और HashMap<Foo,Bar>किसी अन्य के लिए Fooऔर Bar

@SuppressWarnings("unchecked")अपनी नाक का उपयोग करें और रखें। ओह, और जावा में संशोधित जेनरिक के लिए अभियान :)


14
मैं अनपेक्षित NSMutableWhatever पर जावा के संशोधित जेनरिक को ले जाऊंगा, जो सप्ताह के किसी भी दिन दस साल की छलांग की तरह महसूस करता है। कम से कम जावा कोशिश कर रहा है।
दान रोसेनस्टार्क

12
बिल्कुल सही। यदि आप प्रकार की जाँच पर जोर देते हैं, तो यह केवल HashMap <;?;> के साथ किया जा सकता है और यह चेतावनी को नहीं हटाएगा क्योंकि इसके प्रकार सामान्य प्रकार की जाँच नहीं करते हैं। यह दुनिया का अंत नहीं है, लेकिन कष्टप्रद है कि आप या तो एक चेतावनी को दबा रहे हैं या इसके साथ रह रहे हैं।
लहसुन

5
@JonSkeet क्या एक जेनरिक है?
सासक्यू

89

जैसा कि ऊपर दिए गए संदेश बताते हैं, सूची को List<Object>एक List<String>या एक के बीच विभेदित नहीं किया जा सकता है List<Integer>

मैंने इस त्रुटि संदेश को इसी तरह की समस्या के लिए हल किया है:

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

निम्नलिखित के साथ:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

स्पष्टीकरण: पहला प्रकार रूपांतरण यह सत्यापित करता है कि वस्तु एक सूची है जो बिना रखे गए प्रकारों की परवाह किए बिना है (क्योंकि हम सूची स्तर पर आंतरिक प्रकारों को सत्यापित नहीं कर सकते हैं)। अब दूसरे रूपांतरण की आवश्यकता है क्योंकि संकलक केवल यह जानता है कि सूची में कुछ प्रकार की वस्तुएं हैं। यह सूची में प्रत्येक ऑब्जेक्ट के प्रकार की पुष्टि करता है क्योंकि यह एक्सेस है।


3
तुम सही हो मेरे दोस्त सूची डालने के बजाय, बस इसे पुनरावृत्त करें और प्रत्येक तत्व को डालें, चेतावनी दिखाई नहीं देगी, भयानक।
जुआन इज़ा

2
इसने चेतावनी को हटा दिया लेकिन फिर भी मुझे भरोसा नहीं है: पी
मुमैयर

1
डी तो मैं इस और @SuppressWarnings के बीच कोई अंतर नहीं दिख रहा है ( "अनियंत्रित"): हाँ संकलक blindfolding की तरह लगता है, लेकिन क्रम नहीं
channae

1
वह तो कमाल है! @SupressWarning का उपयोग करने का मुख्य अंतर यह है कि यह एनोटेशन का उपयोग करता है जो आपके IDE और कोड विश्लेषण टूल से चेतावनी को समाप्त कर देता है लेकिन यदि आप -Werror फ्लैग कंपाइल का उपयोग कर रहे हैं तो आप अभी तक त्रुटि के साथ समाप्त हो जाएंगे। इस दृष्टिकोण का उपयोग करते हुए दोनों चेतावनी तय की जाती हैं।
एडू कोस्टा

30

एक चेतावनी बस यही है। चेतावनी। कभी-कभी चेतावनियाँ अप्रासंगिक होती हैं, कभी-कभी वे नहीं होती हैं। वे आपका ध्यान किसी ऐसी चीज़ की ओर आकर्षित करने के लिए करते हैं, जो सोचती है कि कंपाइलर एक समस्या हो सकती है, लेकिन हो नहीं सकती।

जातियों के मामले में, यह हमेशा इस मामले में चेतावनी देने वाला है। यदि आप पूरी तरह से निश्चित हैं कि एक विशेष कलाकार सुरक्षित होगा, तो आपको लाइन के ठीक पहले इस तरह एक एनोटेशन जोड़ने पर विचार करना चाहिए (मैं वाक्य रचना के बारे में निश्चित नहीं हूं):

@SuppressWarnings (value="unchecked")

14
-1: एक चेतावनी को कभी स्वीकार नहीं किया जाना चाहिए। या इस तरह की चेतावनियों को दबाएं या ठीक करें। वह क्षण आएगा जहाँ आपको कई चेतावनियाँ देनी होंगी और आप एक बार प्रासंगिक नहीं देखेंगे।
एज़दाज़ुज़ेना

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

9

आपको यह संदेश मिल रहा है क्योंकि getBean एक ऑब्जेक्ट संदर्भ देता है और आप इसे सही प्रकार के लिए कास्टिंग कर रहे हैं। Java 1.5 आपको एक चेतावनी देता है। यह इस तरह से काम करने वाले कोड के साथ जावा 1.5 या बेहतर का उपयोग करने की प्रकृति है। स्प्रिंग का टाइपसेफ़ संस्करण है

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

इसकी टूडू सूची पर।


6

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

उदाहरण के लिए, यदि आप उपयोग करने का प्रयास कर रहे हैं

private Map<String, String> someMap = new HashMap<String, String>();

आप इस तरह का एक नया वर्ग बना सकते हैं

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

फिर जब आप उपयोग करेंगे

someMap = (StringMap) getApplicationContext().getBean("someMap");

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


3

अनियंत्रित चेतावनी से बचने का उपाय:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");

हैक एक समाधान की तरह नहीं दिखता है।
मालविंदर सिंह

1
- क्रमबद्ध वर्ग MyMap एक स्थिर अंतिम क्रमबद्ध घोषित नहीं करता हैवीआईडीयूआईडी टाइप का क्षेत्र लंबा: {
उलटफेर

1

एक अन्य उपाय, यदि आप खुद को उसी वस्तु को बहुत अधिक पसंद करते हैं और आप अपना कोड नहीं डालना चाहते हैं @SupressWarnings("unchecked"), तो एनोटेशन के साथ एक विधि बनाना होगा। इस तरह आप कलाकारों को केंद्रीकृत कर रहे हैं, और उम्मीद है कि त्रुटि की संभावना कम हो जाएगी।

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}

1

नीचे दिए गए कोड के कारण टाइप सुरक्षा चेतावनी

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

वैकल्पिक हल

मापदंडों का उल्लेख किए बिना एक नया मैप ऑब्जेक्ट बनाएं क्योंकि सूची के भीतर रखी गई वस्तु का प्रकार सत्यापित नहीं है।

चरण 1: एक नया अस्थायी मानचित्र बनाएं

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

चरण 2: मुख्य मानचित्र को त्वरित करें

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

चरण 3: अस्थायी मानचित्र को बदल दें और मानों को मुख्य मानचित्र में सेट करें

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }

0

मैंने गलत क्या किया? मैं समस्या को कैसे हल करूं?

यहाँ :

Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");

आप एक विरासत विधि का उपयोग करते हैं जिसे हम आम तौर पर उस रिटर्न के बाद से उपयोग नहीं करना चाहते हैं Object:

Object getBean(String name) throws BeansException;

बीन फैक्ट्री से बीन पाने के लिए (सिंगलटन के लिए) (बनाने के लिए) अनुकूल बनाने की विधि है:

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

इसका उपयोग इस प्रकार है:

Map<String,String> someMap = app.getBean(Map.class,"someMap");

संकलित करेंगे लेकिन फिर भी एक अनियंत्रित रूपांतरण चेतावनी के साथ क्योंकि सभी Mapवस्तुएं आवश्यक Map<String, String>वस्तुएं नहीं हैं।

लेकिन <T> T getBean(String name, Class<T> requiredType) throws BeansException;बीन जेनेरिक कक्षाओं में पर्याप्त नहीं है जैसे कि सामान्य संग्रह क्योंकि पैरामीटर के रूप में एक से अधिक वर्ग को निर्दिष्ट करने की आवश्यकता होती है: संग्रह का प्रकार और इसका सामान्य प्रकार।

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

सेम की घोषणा:

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

सेम इंजेक्शन:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.