क्यों वैकल्पिक है। () कॉलिंग के बिना ।Present () खराब है, लेकिन itter.next () नहीं है?


23

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

    String theFirstString = myCollection.stream()
            .findFirst()
            .get();

यहाँ IntelliJ चेताती है कि () चेकिंग के बिना कहा जाता है (पहले)।

हालाँकि, यह कोड:

    String theFirstString = myCollection.iterator().next();

... कोई चेतावनी नहीं देता।

क्या दो तकनीकों के बीच कुछ गहरा अंतर है, जो स्ट्रीमिंग दृष्टिकोण को किसी तरह से अधिक "खतरनाक" बनाता है, कि कॉलिंग () को कॉल किए बिना (।) पहले कभी नहीं मिलना महत्वपूर्ण है? चारों ओर घूमते हुए, मैं लेखों के बारे में बात कर रहा हूं कि प्रोग्रामर वैकल्पिक <> के साथ कैसे लापरवाह हैं और मान लें कि वे जब भी मिल सकते हैं () कह सकते हैं।

बात यह है, मैं अपने अपवाद को उस जगह पर फेंकना पसंद करता हूं, जहां मेरा कोड छोटी गाड़ी है, जो इस मामले में होगा जहां मुझे कॉल मिल रहा है () जब कोई तत्व नहीं मिलता है। मुझे बेकार निबंध लिखने की ज़रूरत नहीं है जैसे:

    Optional<String> firstStream = myCollection.stream()
            .findFirst();
    if (!firstStream.isPresent()) {
        throw new IllegalStateException("There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>");
    }
    String theFirstString = firstStream.get();

... जब तक कि () (मैं) से अनजान अपवादों को भड़काने में कुछ खतरा नहीं है?


कार्ल बेज़ेलफेल्ड की प्रतिक्रिया और हल्क की एक टिप्पणी को पढ़ने के बाद, मुझे एहसास हुआ कि ऊपर दिए गए मेरे अपवाद कोड थोड़ा अनाड़ी था, यहाँ कुछ बेहतर है:

    String errorString = "There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>";
    String firstString = myCollection.stream()
            .findFirst()
            .orElseThrow(() -> new IllegalStateException(errorString));

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


7
यदि आप orElseThrow का उपयोग करते हैं तो अपवाद वाला भाग आपके अंतिम उदाहरण को और अधिक संक्षिप्त लिखा जा सकता है - और यह सभी पाठकों के लिए स्पष्ट होगा कि आप फेंके जाने वाले अपवाद का इरादा रखते हैं।
हल्क

जवाबों:


12

सबसे पहले, विचार करें कि चेक जटिल है और शायद अपेक्षाकृत समय लेने के लिए। इसके लिए कुछ स्थैतिक विश्लेषण की आवश्यकता होती है, और दोनों कॉल के बीच काफी कोड हो सकता है।

दूसरा, दो निर्माणों का मुहावरेदार उपयोग बहुत अलग है। मैं स्कैला में पूरे समय का कार्यक्रम करता हूं, जो Optionsजावा की तुलना में बहुत अधिक बार उपयोग करता है। मैं एक उदाहरण याद नहीं आ रहा है, जहां मैं एक का उपयोग करने के लिए आवश्यक .get()एक पर Option। आपको लगभग हमेशा Optionalअपने फ़ंक्शन से वापस लौटना चाहिए , या orElse()इसके बजाय उपयोग करना चाहिए। अपवादों को फेंकने की तुलना में लापता मूल्यों को संभालने के लिए ये बहुत बेहतर तरीके हैं।

क्योंकि .get()शायद ही कभी सामान्य उपयोग में बुलाया जाना चाहिए, यह एक आईडीई के लिए एक जटिल जांच शुरू करने के लिए समझ में आता है जब यह देखता है .get()कि क्या यह ठीक से उपयोग किया गया था। इसके विपरीत, iterator.next()एक पुनरावृत्त का उचित और सर्वव्यापी उपयोग है।

दूसरे शब्दों में, यह एक के खराब होने की बात नहीं है और दूसरे की नहीं, यह एक ऐसा मामला है जो एक सामान्य मामला है जो इसके संचालन के लिए मौलिक है और डेवलपर परीक्षण के दौरान अपवाद को फेंकने की संभावना अधिक है, और दूसरा शायद ही कभी इस्तेमाल किया जा रहा है। डेवलपर परीक्षण के दौरान अपवाद को फेंकने के लिए पर्याप्त अभ्यास नहीं किया जा सकता है। उन मामलों के प्रकार हैं जिनके बारे में आपको चेतावनी देने के लिए IDEs चाहते हैं।


क्या मुझे इस वैकल्पिक व्यवसाय के बारे में अधिक जानने की आवश्यकता हो सकती है - मैंने ज्यादातर java8 सामान को वस्तुओं की सूचियों की मैपिंग करने के लिए एक अच्छा तरीका के रूप में देखा है जो हम अपने वेबप में बहुत कुछ करते हैं। तो आप वैकल्पिक <> हर जगह भेजने वाले हैं? यह कुछ करने के लिए इस्तेमाल हो रही ले जाएगा, लेकिन यह एक और एसई पोस्ट है! :)
टीवी का फ्रैंक

@ टीवी के फ्रैंक यह कम है कि वे है कि वे आप कार्यों कि निहित प्रकार का एक मूल्य बदलने लिखने की अनुमति है, और आप के साथ उन्हें श्रृंखला हर जगह होना चाहिए रहे हैं, और अधिक mapया flatMap। चेकों के Optionalसाथ सब कुछ ब्रैकेट से बचने के लिए ज्यादातर एक शानदार तरीका है null
मॉर्गन

15

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

तर्क है कि पुनरावृत्तियों के लिए एक समान चेतावनी उत्पन्न की जानी चाहिए। हालाँकि, यह बस कभी नहीं किया गया है, और एक को जोड़ने से शायद मौजूदा परियोजनाओं में बहुत अधिक चेतावनी हो सकती है एक अच्छा विचार है।

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


6

मैं मानता हूं कि इनमें कोई अंतर्निहित अंतर नहीं है, हालांकि, मैं एक बड़ा दोष बताना चाहूंगा।

दोनों ही मामलों में, आपके पास एक प्रकार है, जो एक ऐसी विधि प्रदान करता है जो काम करने की गारंटी नहीं है और आपको इससे पहले किसी अन्य विधि को कॉल करना चाहिए। क्या आपका आईडीई / संकलक / आदि आपको एक मामले के बारे में चेतावनी देता है या दूसरा सिर्फ इस बात पर निर्भर करता है कि वह इस मामले का समर्थन करता है और / या आपने ऐसा करने के लिए इसे कॉन्फ़िगर किया है।

हालांकि कहा जा रहा है कि कोड के दोनों टुकड़े कोड बदबू आ रहे हैं। ये अभिव्यक्तियाँ अंतर्निहित डिज़ाइन दोषों और उपज भंगुर कोड पर संकेत देती हैं।

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

आपका संग्रह अभी के लिए गैर-रिक्त होना जाना जा सकता है, लेकिन आप सभी पर वास्तव में भरोसा कर सकते हैं इसका प्रकार है: Collection<T>- और यह आपको गैर-शून्यता की गारंटी नहीं देता है। भले ही आप जानते हैं कि यह अब गैर-रिक्त है, एक बदली हुई आवश्यकता के कारण कोड अपफ्रंट को बदला जा सकता है और अचानक आपके सभी कोड एक खाली संग्रह से टकरा जाते हैं।

अब आप पीछे धकेलने के लिए स्वतंत्र हैं और दूसरों को बताते हैं कि आप एक गैर-रिक्त सूची प्राप्त करने वाले थे। आप खुश हो सकते हैं कि आप उस 'त्रुटि' को तेजी से पाते हैं। फिर भी, आपके पास सॉफ़्टवेयर का टूटा हुआ टुकड़ा है और आपको अपना कोड बदलने की आवश्यकता है।

इसे अधिक व्यावहारिक दृष्टिकोण के साथ विरोधाभास करें: चूंकि आपको एक संग्रह मिलता है और यह संभवतः खाली हो सकता है, इसलिए आप #get को छोड़कर वैकल्पिक # मानचित्र, #orElse या इन विधियों में से किसी अन्य विधि का उपयोग करके उस मामले से निपटने के लिए खुद को मजबूर करते हैं।

कंपाइलर आपके लिए जितना संभव हो उतना काम करता है जैसे कि आप एक प्राप्त करने के स्थैतिक-प्रकार के अनुबंध पर जितना संभव हो उतना भरोसा कर सकते हैं Collection<T>। जब आपका कोड कभी भी इस अनुबंध का उल्लंघन नहीं करता है (जैसे कि इन दो बदबूदार कॉल श्रंखलाओं में से), तो आपका कोड पर्यावरणीय परिस्थितियों को बदलने के लिए बहुत कम भंगुर है।

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

इसकी लागत यह है कि आपको बेहतर डिजाइन में समय का निवेश करना होगा, क्योंकि एक) भंगुर कोड या बी) के विपरीत, अपवादों को इधर-उधर फेंककर अनावश्यक रूप से जटिल किया जाता है।

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


मुझे लगता है कि मैं कोड गंध के बारे में थोड़ा सहमत हो सकता हूं - मैंने एक छोटा पद बनाने के लिए ऊपर उदाहरण दिया। एक और अधिक वैध मामला एक सूची से एक अनिवार्य तत्व को गिराने का हो सकता है, जो एक आवेदन आईएमओ में दिखाई देने के लिए एक अजीब बात नहीं है।
टीवी का फ्रैंक

सटीक। "सूची से संपत्ति p के साथ तत्व चुनें" -> list.stream ()। Filter (p) .findFirst () .. और फिर भी हम सवाल पूछने के लिए मजबूर हैं "अगर इसमें नहीं है तो क्या होगा?" .. दैनिक रोटी और मक्खन। यदि यह अनिवार्य है और "बस वहीं" - तो आपने इसे पहले अन्य सामान के एक समूह में क्यों छिपाया?
फ्रैंक

4
क्योंकि शायद यह एक सूची है जो एक डेटाबेस से भरी हुई है। हो सकता है कि सूची एक सांख्यिकीय संग्रह है जिसे किसी अन्य फ़ंक्शन में इकट्ठा किया गया था: हम जानते हैं कि हमारे पास ए, बी और सी हैं, मैं बी के लिए जो कुछ भी मान्य है (और मेरे आवेदन में, सामान्य) मामलों की मात्रा खोजना चाहता हूं।
टीवी का फ्रैंक

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