2015-08-29 10 views
18

मैं varargs heap pollution के बारे में पढ़ रहा था और मुझे वास्तव में यह नहीं पता कि कैसे वर्गार्ग या गैर-पुन: प्रयोज्य प्रकार उन समस्याओं के लिए जिम्मेदार होंगे जो पहले से ही सामान्यता के बिना मौजूद नहीं हैं। दरअसल, मैं बहुत आसानी से साथvarargs ढेर प्रदूषण: बड़ा सौदा क्या है?

public static void faultyMethod(String... l) { 
    Object[] objectArray = l; // Valid 
    objectArray[0] = 42; // ArrayStoreException thrown here 
    String s = l[0]; 
} 

दूसरा एक बस सरणियों के सहप्रसरण, जो वास्तव में समस्या यहाँ है का उपयोग करता है

public static void faultyMethod(List<String>... l) { 
    Object[] objectArray = l; // Valid 
    objectArray[0] = Arrays.asList(42); 
    String s = l[0].get(0); // ClassCastException thrown here 
} 

बदल सकते हैं। (भले ही List<String> पुन: प्रयोज्य था, मुझे लगता है कि यह अभी भी Object का उप-वर्ग होगा और मैं अब भी किसी ऑब्जेक्ट को सरणी में असाइन करने में सक्षम हूं।) बेशक मैं देख सकता हूं कि दोनों के बीच थोड़ा अंतर है, लेकिन यह कोड दोषपूर्ण है चाहे वह जेनेरिक का उपयोग करता है या नहीं।

वे ढेर प्रदूषण से क्या मतलब है (यह मेरे स्मृति उपयोग लेकिन समस्या सिर्फ वे के बारे में संभावित प्रकार unsafetiness है बात के बारे में सोचना है), और यह कैसे सरणियों 'सहप्रसरण का उपयोग कर किसी भी प्रकार के उल्लंघन से अलग है?

+0

अच्छा सवाल, मुझे लाइन ऑब्जेक्ट जोड़ने की अनुमति दें [0] = 42; वास्तव में ArrayStoreException फेंक रहा है। – Victor

उत्तर

12

आप सही हैं कि आम (और मौलिक) समस्या सरणी के कॉन्वर्सिस के साथ है। लेकिन आपके द्वारा दिए गए दो उदाहरणों में से पहला सबसे खतरनाक है, क्योंकि आपके डेटा संरचनाओं को संशोधित कर सकते हैं और उन्हें ऐसे राज्य में डाल सकते हैं जो बाद में टूट जाएगा।

यदि आपका पहला उदाहरण ClassCastException ट्रिगर नहीं था पर विचार करें:

List<String> firstList = Arrays.asList("hello", "world"); 
List<String> secondList = Arrays.asList("hello", "dolly"); 
faultyMethod(firstList, secondList); 
return secondList.isEmpty() 
    ? firstList 
    : secondList; 

तो अब हम एक List<String> कि वास्तव में एक Integer शामिल है, और:

public static void faultyMethod(List<String>... l) { 
    Object[] objectArray = l;   // Valid 
    objectArray[0] = Arrays.asList(42); // Also valid 
} 

और यहाँ कैसे किसी को इसे इस्तेमाल करता है यह सुरक्षित रूप से चारों ओर तैर रहा है। कुछ बिंदु बाद — संभवतया बहुत बाद में, और यदि यह क्रमबद्ध है, तो संभवतः अधिक बाद में और एक अलग JVM — में कोई अंततः String s = theList.get(0) निष्पादित करता है। यह विफलता इतनी दूर है कि इससे क्या पता चला कि इसे ट्रैक करना बहुत मुश्किल हो सकता है।

ध्यान दें कि क्लासकास्टएक्सप्शन का स्टैक ट्रेस हमें नहीं बताता है कि वास्तव में त्रुटि कहां हुई; यह सिर्फ हमें बताता है कि इसे किसने ट्रिगर किया। दूसरे शब्दों में, यह हमें बग को ठीक करने के तरीके के बारे में अधिक जानकारी नहीं देता है; और यही कारण है कि यह एक ArrayStoreException की तुलना में एक बड़ा सौदा बनाता है।

+0

हाँ, यह निष्कर्ष है कि मैं दो अन्य उत्तरों को पढ़ने के लिए आया था, जो इसे स्पष्ट रूप से आपके रूप में नहीं बताते हैं लेकिन मुझे इसे समझने में मदद मिली। हंपफ, मुझे विजेता चुनने में मुश्किल होगी। – Dici

+0

@yshavit, मैंने आपके उत्तर का अध्ययन किया है और इसे बहुत दिलचस्प पाया है, साझा करने के लिए धन्यवाद ... यदि आप विधि 'faultyMethod (सूची .... एल) विधि का उपयोग करते हैं, तो मैं जोड़ना चाहूंगा। तत्वों को 'faultyMethod (firstList, secondList)' या अज्ञात सरणी जैसे 'faultyMethod (नई सूची [] {firstList, secondList}) के रूप में अलग-अलग तत्वों के रूप में पास करना,' आप विधि के बाहर कोई समस्या चाहते हैं क्योंकि ऑब्जेक्ट को संशोधित किया जा रहा है सूचियों को स्पष्ट या निहित (वर्ग तर्क आपके लिए ऐसा करते हैं) जो विधि एकमात्र तर्क के रूप में कार्य करता है। – Victor

+0

मेरा मतलब है कि यदि आप इस तरह की चीजें करते हैं तो आपको फ़ंक्शन के दायरे से बाहर समस्याएं होती हैं: 'सूची [] सरणी = नई सूची [] {firstList, secondList}; दोषपूर्ण विधि (सरणी); System.out.println (सरणी [0] + "" + सरणी [1]); ' – Victor

7

एक सरणी और सूची के बीच का अंतर यह है कि सरणी इसके संदर्भों की जांच करती है। जैसे

Object[] array = new String[1]; 
array[0] = new Integer(1); // fails at runtime. 

तथापि

List list = new ArrayList<String>(); 
list.add(new Integer(1)); // doesn't fail. 
+2

हाँ, मुझे यह पता था। मुझे लगता है कि इसका मतलब क्या है कि सरणी को सुरक्षित माना जाता है क्योंकि मैं रनटाइम ('ArrayStoreException') पर उसे चाल नहीं कर पाऊंगा, जबकि 'सूची' शिकायत नहीं करेगा, और इसलिए वे संकलन समय पर एक चेतावनी उत्पन्न करते हैं? – Dici

+0

@Dici हाँ, यही मेरा मतलब था। +1 –

5

जुड़ा हुआ दस्तावेज़ से, मेरा मानना ​​है कि ओरेकल द्वारा "ढेर प्रदूषण" क्या मतलब डेटा मानों कि तकनीकी रूप से JVM विनिर्देश द्वारा अनुमति दी जाती है के लिए है, लेकिन नियमों के अनुसार की अनुमति नहीं है जावा प्रोग्रामिंग भाषा में जेनेरिक के लिए।

आपको एक उदाहरण देता करने के लिए, मान लीजिए कि हम इस तरह एक साधारण List कंटेनर को परिभाषित करते हैं:

List<String> lst = new List<String>(); 
lst.add("abc"); 

यह एक है:

class List<E> { 
    Object[] values; 
    int len = 0; 

    List() { values = new Object[10]; } 

    void add(E obj) { values[len++] = obj; } 
    E get(int i) { return (E)values[i]; } 
} 

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

String x = (String)lst.values[0]; 

मोड़ - अब यहाँ कोड है कि कच्चे प्रकार के साथ काम करता है और कुछ बुरा करता है, के कारण "ढेर प्रदूषण" है:

lst.values[lst.len++] = new Integer("3"); 

कोड ऊपर काम करता है क्योंकि सरणी, प्रकार Object[] की है जो एक स्टोर कर सकते हैं Integer

String y = lst.get(1); // ClassCastException for Integer(3) -> String 

ध्यान दें कि ClassCastException हमारे वर्तमान में क्या होता है: पुनर्प्राप्ति समय (जो भ्रष्टाचार हुआ के बाद जिस तरह से है) पर, के बजाय जोड़ने में समय - अब जब हम मान प्राप्त करने का प्रयास करें, यह एक ClassCastException कारण जाएगा स्टैक फ्रेम, List.get() में भी नहीं, क्योंकि List.get() में कास्ट जावा के प्रकार एरर सिस्टम के कारण रन-टाइम पर नो-ऑप है।

असल में, हमने Integer को List<String> में जेनिक्स को छोड़कर डाला।फिर जब हमने get() तत्व को करने का प्रयास किया, तो सूची वस्तु अपने वादे को कायम रखने में विफल रही कि उसे String (या null) वापस करना होगा।

+0

हां, मुझे इसके बारे में पता है, लेकिन मुझे लगता है कि आपके साथ जवाब और पीटर लॉरी का एक मैं देख सकता हूं कि उन्होंने ऐसा क्यों किया। Arrays प्रविष्टि के समय एक अपवाद सही फेंक देंगे जबकि एक सामान्य सूची (उदाहरण के लिए) केवल मूल्य को पढ़ने पर असफल हो जाएगी, जो हो सकता है या प्रविष्टि के बाद लंबे समय तक नहीं हो सकता है, जिससे डीबग कठिन हो जाता है। क्या इसका मतलब है? – Dici

2

जेनरिक से पहले, बिल्कुल कोई संभावना नहीं थी कि ऑब्जेक्ट का रनटाइम प्रकार इसके स्थिर प्रकार के साथ असंगत है। यह स्पष्ट रूप से एक बहुत वांछनीय संपत्ति है।

हम किसी ऑब्जेक्ट को गलत रनटाइम प्रकार पर डाल सकते हैं, लेकिन कलाकार कास्टिंग की सटीक साइट पर तत्काल विफल हो जाएगा; त्रुटि वहां रुक जाती है।

Object obj = "string"; 
((Integer)obj).intValue(); 
// we are not gonna get an Integer object 
जेनेरिक्स के परिचय, प्रकार विलोपन (सभी बुराइयों की जड़) के साथ साथ

, अब यह संभव है कि एक विधि संकलन समय पर रिटर्न String, अभी तक रनटाइम पर Integer देता है। यह गड़बड़ है। और हमें स्रोत से इसे रोकने के लिए हम सब कुछ कर सकते हैं। यही कारण है कि संकलक अनचेक जानवरों की हर दृष्टि के बारे में इतना मुखर है।

ढेर प्रदूषण के बारे में सबसे बुरी बात यह है कि रनटाइम व्यवहार अपरिभाषित है! विभिन्न कंपाइलर/रनटाइम प्रोग्राम को विभिन्न तरीकों से निष्पादित कर सकता है। case1 और case2

+0

धन्यवाद। अब जब मेरा मन इस पर स्पष्ट है, मुझे लगता है कि पुल विधियों को अजीब 'क्लासकास्ट अपवाद' के लिए ज़िम्मेदार होना चाहिए, कभी-कभी मुझे स्पार्क के साथ बंद करने के क्रम में मिलता है – Dici

1

वे अलग हैं क्योंकि ClassCastException और ArrayStoreException अलग हैं।

जेनिक्स संकलन-समय प्रकार जांच नियमों को यह सुनिश्चित करना चाहिए कि ClassCastException ऐसी जगह पर प्राप्त करना असंभव है जहां आपने एक स्पष्ट कलाकार नहीं डाला है, जब तक कि आपका कोड (या कुछ कोड जिसे आपने बुलाया या बुलाया) कुछ असुरक्षित था संकलन-समय, इस मामले में आपको (या असुरक्षित चीज़ जो भी कोड चाहिए) को इसके बारे में एक संकलन-समय चेतावनी प्राप्त करनी चाहिए।

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

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