जावा 8 के वैकल्पिक का उपयोग तर्कों में क्यों नहीं किया जाना चाहिए


392

मैंने कई वेब साइटों पर पढ़ा है वैकल्पिक का उपयोग केवल रिटर्न प्रकार के रूप में किया जाना चाहिए, और विधि तर्कों में उपयोग नहीं किया जाना चाहिए। मैं तार्किक कारण खोजने के लिए संघर्ष कर रहा हूं। उदाहरण के लिए मेरे पास तर्क का एक टुकड़ा है जिसमें 2 वैकल्पिक पैरामीटर हैं। इसलिए मुझे लगता है कि इस तरह से मेरी विधि हस्ताक्षर लिखने के लिए समझ में आएगा (समाधान 1):

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
    // my logic
}

कई वेब पेज निर्दिष्ट करते हैं कि वैकल्पिक का उपयोग विधि तर्कों के रूप में नहीं किया जाना चाहिए। इसे ध्यान में रखते हुए, मैं निम्नलिखित विधि हस्ताक्षर का उपयोग कर सकता हूं और यह स्पष्ट करने के लिए एक स्पष्ट Javadoc टिप्पणी जोड़ सकता हूं कि तर्क अशक्त हो सकते हैं, उम्मीद है कि भविष्य के रखरखावकर्ता Javadoc पढ़ेंगे और इसलिए हमेशा तर्कों का उपयोग करने से पहले शून्य जांच करें (समाधान 2) :

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

वैकल्पिक रूप से मैं अपने तरीके को चार सार्वजनिक तरीकों से बदल सकता हूं ताकि एक अच्छा इंटरफ़ेस प्रदान किया जा सके और इसे अधिक स्पष्ट किया जा सके p1 और P2 वैकल्पिक हैं (समाधान 3):

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

अब मैं कक्षा का कोड लिखने का प्रयास करता हूं जो प्रत्येक दृष्टिकोण के लिए तर्क के इस टुकड़े को आमंत्रित करता है। मैं पहली बार दो इनपुट पैरामीटर्स को किसी अन्य ऑब्जेक्ट से पुनः प्राप्त करता Optionalहूं, जो एस रिटर्न करता है और फिर, मैं आह्वान करता हूं calculateSomething। इसलिए, यदि समाधान 1 का उपयोग किया जाता है तो कॉलिंग कोड इस तरह दिखाई देगा:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

यदि समाधान 2 का उपयोग किया जाता है, तो कॉलिंग कोड इस तरह दिखाई देगा:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

यदि समाधान 3 लागू होता है, तो मैं ऊपर दिए गए कोड का उपयोग कर सकता हूं या मैं निम्नलिखित का उपयोग कर सकता हूं (लेकिन यह काफी अधिक कोड है):

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

तो मेरा सवाल यह है Optionalकि पद्धति तर्कों के रूप में s का उपयोग करना बुरा क्यों माना जाता है (देखें समाधान 1)? यह मेरे लिए सबसे पठनीय समाधान की तरह दिखता है और यह सबसे स्पष्ट करता है कि पैरामीटर भविष्य के रखरखाव के लिए खाली / अशक्त हो सकते हैं। (मुझे पता है कि Optionalइसका उद्देश्य केवल एक वापसी प्रकार के रूप में इस्तेमाल किया जाना है, लेकिन मुझे इस परिदृश्य में इसका उपयोग नहीं करने के लिए कोई तार्किक कारण नहीं मिल सकता है)।


16
यदि आप वैकल्पिक का उपयोग करते हैं, तो क्या आपको यह जांचना होगा कि वैकल्पिक पैरामीटर के रूप में पारित नहीं हुआ है null?
biziclop

17
हां, लेकिन यह भविष्य में कोड को बनाए रखने वाले कुछ अन्य लोगों के लिए स्पष्ट कर देगा कि पैरामीटर खाली / अशक्त हो सकता है, इसलिए संभावित रूप से भविष्य में एक शून्य सूचक अपवाद से बचा रहा है
नील स्टीवंस

1
वाह। अशक्त तर्क एक विधि में पारित किए जा रहे हैं? ऐसा विजुअल बेसिक है। यह किस सिद्धांत का उल्लंघन कर रहा है? एसआरपी (शायद)। यह एक अन्य सिद्धांत का भी उल्लंघन करता है, जिसका नाम मुझे छोड़ देता है, अपना काम करने के लिए किसी विधि या कार्य के लिए केवल आवश्यक जानकारी में गुजरने की तर्ज पर चला जाता है।
चमकदार

20
सैद्धांतिक रूप से संभवतः सब कुछ शून्य होना पुस्तकालय में हर विधि की तरह है, जिसे संभवतः System.exit (0) कहा जाता है। आप tis के खिलाफ जाँच नहीं कर सकते हैं और आपको इसके विरुद्ध जाँच नहीं करनी चाहिए। सब कुछ आप वास्तव में आप (लगभग) कभी नहीं करना चाहिए हर समय करना होगा। जैसे सभी मापदंडों को अंतिम बनाना। अपने उपकरणों को पैरामीटर मान बदलने से रोकने में मदद करें या अपने कोड को हजार फ़ाइनल द्वारा और कई अशक्त जाँचों के बजाय अपने कोड को अपठनीय बनाने के लिए आरंभ करने के लिए भूल जाएं।
Yeoman

6
वास्तव में सिर्फ नॉनन्यू / न्यूलीटेबल एनोटेशन का उपयोग करें, यही आप इस स्थिति में देख रहे हैं, वैकल्पिक नहीं।
बिल के

जवाबों:


202

ओह, उन कोडिंग शैलियों को थोड़ा नमक के साथ लिया जाना है।

  1. (+) वैकल्पिक पद्धति से किसी भी अर्थ विश्लेषण के बिना किसी अन्य विधि में उत्तीर्ण होना; इस विधि को छोड़ना, बिलकुल ठीक है।
  2. (-) तरीकों के अंदर सशर्त तर्क के कारण वैकल्पिक मापदंडों का उपयोग करना वस्तुतः गर्भ-उत्पादक है।
  3. (-) एक वैकल्पिक में एक तर्क को पैक करने की आवश्यकता है, संकलक के लिए उप-समरूप है, और एक अनावश्यक रैपिंग करता है।
  4. (-) अशक्त मापदंडों की तुलना में वैकल्पिक अधिक महंगा है।

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


38
वास्तव में, एक होने Optionalएक: पैरामीटर तीन राज्यों में से एक का प्रतिनिधित्व करता है nullवैकल्पिक, एक गैर-शून्य Optionalके साथ isPresent() == falseऔर एक गैर-शून्य Optionalएक वास्तविक मूल्य लपेटकर।
biziclop

16
@biziclop हां, एक अपरिहार्य बिंदु जो पहले ही आलोचना कर चुका है। लेकिन इरादा केवल गैर-अशक्त भाव रखना है। यह लंबे समय तक नहीं लिया, कुछ मामलों में भी वैकल्पिक से बचने के लिए सलाह सुनने के लिए, काफी विडंबना है। ए @NotNull Optional
जोफ एगेन

80
@biziclop ध्यान दें कि आप उपयोग कर रहे हैं Optionalसब पर है, तो राज्य 1 ( nullवैकल्पिक) आम तौर पर एक प्रोग्रामिंग त्रुटि इंगित करता है, तो आप के रूप में अच्छी इसे संभाल नहीं हो सकता है (बस जाने यह एक फेंक NullPointerException)
user253751

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

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

165

इस विषय पर मैंने जो सबसे अच्छी पोस्ट देखी, वह डैनियल ओल्स्ज़वेस्की द्वारा लिखी गई थी :

यद्यपि यह अनिवार्य विधि मापदंडों के लिए वैकल्पिक पर विचार करने के लिए आकर्षक हो सकता है, इस तरह के एक समाधान अन्य संभावित विकल्पों की तुलना में पीला। समस्या का वर्णन करने के लिए, निम्नलिखित निर्माणकर्ता घोषणा की जाँच करें:

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

पहली नज़र में यह एक सही डिज़ाइन निर्णय के रूप में लग सकता है। आखिरकार, हमने अनुलग्नक पैरामीटर को वैकल्पिक रूप से स्पष्ट रूप से चिह्नित किया। हालांकि, कंस्ट्रक्टर को कॉल करने के लिए, क्लाइंट कोड थोड़ा सा अनाड़ी हो सकता है।

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

स्पष्टता प्रदान करने के बजाय, वैकल्पिक वर्ग की फ़ैक्टरी विधियाँ केवल पाठक को विचलित करती हैं। नोट केवल एक ही वैकल्पिक पैरामीटर है, लेकिन दो या तीन होने की कल्पना करें। अंकल बॉब निश्चित रूप से ऐसे कोड पर गर्व नहीं करेंगे be

जब कोई विधि वैकल्पिक मापदंडों को स्वीकार कर सकती है, तो यह अच्छी तरह से सिद्ध दृष्टिकोण को अपनाने और इस तरह के मामले को ओवरलोडिंग का उपयोग करके डिजाइन करने के लिए बेहतर है। SystemMessage वर्ग के उदाहरण में, दो अलग-अलग निर्माणकर्ताओं की घोषणा करना वैकल्पिक उपयोग करने के लिए बेहतर है।

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

वह परिवर्तन क्लाइंट कोड को बहुत सरल और पढ़ने में आसान बनाता है।

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);

10
जब केवल एक या दो पैराग्राफ प्रासंगिक हों, तो यह बहुत सी कॉपी + पेस्ट है।
गैरेट विल्सन

47
दुर्भाग्य से यह स्पष्टीकरण कॉल करने वाले की चिंता को संबोधित नहीं करता है, उदाहरण के लिए, कुछ जानकारी को पार्स करना वैकल्पिक हो सकता है। ओवरलोडिंग की विधि के साथ (जैसा कि ऊपर बताया गया है), कॉलिंग कोड को यह कहना है, "क्या X मौजूद है? यदि हां, तो मैं ओवरलोडेड विधि को कॉल करूंगा। क्या ए वर्तमान में है? मुझे ओवरलोडेड विधि को कॉल करना होगा। यदि X और Y मौजूद हैं, मुझे ओवरलोडेड विधि C को कॉल करना होगा। " और इसी तरह। इसका एक अच्छा जवाब हो सकता है, लेकिन "क्यों" का यह स्पष्टीकरण इसे कवर नहीं करता है।
गैरेट विल्सन

9
इसके अलावा, जब संग्रह की बात आती है, तो एक खाली संग्रह और कोई संग्रह नहीं होने के बीच एक अलग अंतर होता है। उदाहरण के लिए, एक कैश। क्या यह कैश मिस था? खाली वैकल्पिक / अशक्त। क्या कोई कैश हिट था जो एक खाली संग्रह होता है? पूर्ण वैकल्पिक / संग्रह।
अजाक्स

1
@ अजाक्स मुझे लगता है कि आप लेख को गलत समझ रहे हैं। वे एक बिंदु द्वारा की वकालत के हवाले कर रहे हैं प्रभावी जावा पुस्तक, जो कहते हैं कि जब एक विधि वापसी प्रकार एक संग्रह (नहीं एक कैश वापसी होगी के रूप में एक मनमाना वस्तु) है तो आप के बजाय एक खाली संग्रह लौटने के पक्ष में होना चाहिए nullया Optional
गिली

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

86

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

कार्यात्मक प्रोग्रामिंग भाषाएं Optionalमापदंडों को प्रोत्साहित करती हैं। इसका उपयोग करने के सर्वोत्तम तरीकों में से एक है कई वैकल्पिक पैरामीटर और liftM2एक फ़ंक्शन का उपयोग करने के लिए मानने वाले मानदंड रिक्त नहीं हैं और एक वैकल्पिक लौटना (देखें http://www.functionaljava.org/javadoc/4.4/functionaljava/fj data / Option.html # LifM2-fj.F- )। जावा 8 ने दुर्भाग्यवश वैकल्पिक का समर्थन करते हुए एक बहुत सीमित पुस्तकालय को लागू किया है।

जावा प्रोग्रामर के रूप में हमें केवल विरासत पुस्तकालयों के साथ बातचीत करने के लिए अशक्त का उपयोग करना चाहिए।


3
इसके बजाय javax.annotation का उपयोग @Nonnullऔर उपयोग कैसे किया @Nullableजाता है? मैं उनके द्वारा विकसित एक पुस्तकालय में बड़े पैमाने पर उपयोग करता हूं, और आईडीई (इंटेलीज) द्वारा प्रदान किया गया समर्थन बहुत अच्छा है।
रोजेरियो

1
यह ठीक है, लेकिन आपको हर जगह इस एनोटेशन का उपयोग करना होगा और आपको मैप, फ्लैटपाइप / बाइंड, लिफ्टएम 2, सीक्वेंस आदि जैसे तरीकों का समर्थन करने के लिए एक लाइब्रेरी की आवश्यकता होगी
मार्क पेरी

14
कार्यात्मक प्रोग्रामिंग भाषाएं वैकल्पिक मापदंडों को प्रोत्साहित करती हैं। प्रशस्ति पत्र की जरूरत। अधिकांश कार्यों को वैकल्पिक नहीं लेना चाहिए; इसके बजाय, मूल्य की उपस्थिति या अनुपस्थिति के साथ व्यवहार (उपयुक्त उच्च-क्रम के कार्यों का उपयोग करने) का प्रश्न में फ़ंक्शन के कॉलर पर है।
जुब। 22

5
इस। वास्तव में, कोई भी उत्तर पर्याप्त रूप से आश्वस्त नहीं करता है, और उनमें से कोई भी इस विशिष्ट प्रश्न का उत्तर नहीं देता है "क्यों विधि मापदंडों के रूप में वैकल्पिक का उपयोग करना बुरा व्यवहार माना जाता है जबकि विधि मापदंडों की व्याख्या @NonNullकरना पूरी तरह से ठीक है यदि वे अलग-अलग तरीकों से एक ही उद्देश्य की सेवा करते हैं?" मेरे लिए, केवल एक तर्क है जो कम से कम कुछ समझ में आता है: "वैकल्पिक का उपयोग बेहतर एपीआई प्रदान करने के लिए किया जाना चाहिए, जिसमें स्पष्ट वापसी प्रकार हो। हालांकि, विधि तर्कों के लिए, अतिभारित कार्यों का उपयोग किया जा सकता है।" - फिर भी, मुझे नहीं लगता कि यह एक तर्क है जो काफी मजबूत है।
उत्क्कू

1
बेशक, अशक्त करने के लिए बेहतर है, लेकिन क्या और भी बेहतर है पहली जगह में वैकल्पिक मूल्यों की एक बहुत जरूरत नहीं है क्योंकि अच्छा डिजाइन: डी
तुमानी

12

यह सलाह, "अंगूठे के नियम के रूप में इनपुट के बारे में जितना संभव हो उतना संभव नहीं है और आउटपुट के संबंध में विशिष्ट है।"

आमतौर पर यदि आपके पास एक ऐसा तरीका है जो एक सादे गैर-शून्य मान लेता है, तो आप इसे मैप कर सकते हैं Optional, इसलिए इनपुट के संबंध में सादे संस्करण सख्ती से अधिक अनिर्दिष्ट है। हालाँकि , संभावित कारणों का एक गुच्छा है कि आप Optionalफिर भी तर्क की आवश्यकता क्यों चाहेंगे :

  • आप चाहते हैं कि आपका फ़ंक्शन किसी अन्य API के साथ संयोजन में उपयोग किया जाए जो एक रिटर्न देता है Optional
  • आपका फ़ंक्शन खाली के अलावा कुछ और लौटाना चाहिए Optionalयदि दिया गया मान खाली है
  • आपको लगता Optionalहै कि बहुत बढ़िया है कि जो कोई भी आपके एपीआई का उपयोग करता है, उसे इसके बारे में जानने के लिए आवश्यक होना चाहिए ;-)

10

साथ पैटर्न Optionalके लिए से बचने के लिए एक है लौटने null । यह अभी भी nullएक विधि में पारित करने के लिए पूरी तरह से संभव है ।

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

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}

यहाँ समस्या यह है कि @NotNull डिफ़ॉल्ट मामला नहीं है और इसे स्पष्ट रूप से एनोटेट करना है - इसलिए बॉयलरप्लेट और सामान जो लोग सिर्फ आलस्य के कारण नहीं करेंगे।
dwegener

1
@dwegener हाँ, लेकिन Optionalसमस्या को ठीक नहीं करता है, इसलिए एनोटेशन के लिए मेरा सुझाव है।
मकोतो

2
वैकल्पिक को अनदेखा करना बहुत कठिन है क्योंकि यह एक एनोटेशन को अनदेखा करना है जिसे आप केवल तभी नोटिस करेंगे जब डॉक्स / स्रोत पढ़ रहे हों या यदि आपका टूलिंग इसे पकड़ सके (स्थैतिक विश्लेषण हमेशा अशक्तता को सही ढंग से निर्धारित नहीं कर सकता है)।
अजाक्स

ध्यान दें कि @ निलेबल का अर्थ यह नहीं हो सकता है कि आप एक वैध मान के रूप में "स्वीकार" करें, अर्थात कोई अपवाद न फेंके। इसका सिर्फ यह मतलब हो सकता है कि आप इस मामले से बचते हैं, लेकिन फिर भी एक अपवाद को छोड़ देंगे (यह फाइंडबग शब्दार्थ है)। तो आप स्पष्टता के बजाय इस तरह के एनोटेशन के साथ अस्पष्टता का परिचय दे सकते हैं।
trruse

मुझे यकीन नहीं है कि @ user2986984 क्या अस्पष्टता है। "नहीं संभालना अशक्त" केवल वास्तविक मतलब हो सकता है एक अपवाद फेंक दिया है।
माकोटो

7

यह मुझे थोड़ा मूर्खतापूर्ण लगता है, लेकिन एकमात्र कारण जो मैं सोच सकता हूं, वह यह है कि विधि मापदंडों में ऑब्जेक्ट तर्क पहले से ही एक तरह से वैकल्पिक हैं - वे अशक्त हो सकते हैं। इसलिए किसी को किसी मौजूदा वस्तु को लेने के लिए मजबूर करना और इसे वैकल्पिक में लपेटना व्यर्थ की तरह है।

कहा जा रहा है, वैकल्पिक तरीकों को एक साथ ले जाना / वापस लेना एक उचित कार्य करना है, जैसे कि शायद मोनड।


7
क्या मान और क्षेत्र वापस नहीं आ सकते हैं? तो Optionalपूरी तरह से व्यर्थ है?
शमूएल एडविन वार्ड

6

JavaDoc को JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html पर देखें , एक एपीआई नोट जोड़ा गया है:

एपीआई नोट: वैकल्पिक रूप से एक विधि रिटर्न प्रकार के रूप में उपयोग करने के लिए अभिप्रेत है, जहां "कोई परिणाम नहीं" का प्रतिनिधित्व करने के लिए एक स्पष्ट आवश्यकता है, और जहां अशक्त का उपयोग त्रुटियों की संभावना है।


4

मेरा लेना यह है कि वैकल्पिक एक मोनाड होना चाहिए और ये जावा में बोधगम्य नहीं हैं।

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

अमूर्तता के इन दो स्तरों को मिलाने से सुगमता नहीं होती है इसलिए आप इसे टालने से बेहतर हैं।


3

Optionalपैरामीटर के रूप में पास होने पर सावधानी से रहने का एक और कारण यह है कि एक विधि को एक काम करना चाहिए ... यदि आप एक Optionalपरम को पास करते हैं तो आप एक से अधिक काम करने का पक्ष ले सकते हैं, यह बूलियन परम को पारित करने के समान हो सकता है।

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }

मुझे लगता है कि यह एक अच्छा कारण नहीं है। यदि वैकल्पिक का उपयोग नहीं किया जाता है, तो परम को शून्य होने की जाँच के लिए एक ही तर्क लागू किया जा सकता है। वास्तव में, if(param.isPresent())वैकल्पिक उपयोग करने के लिए सबसे अच्छा तरीका नहीं है, इसके बजाय आप उपयोग कर सकते हैं:param.forEach(() -> {...})
hdkrus

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

2

मापदंडों के रूप में वैकल्पिक को स्वीकार करना कॉलर स्तर पर अनावश्यक लपेटन का कारण बनता है।

उदाहरण के लिए:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

मान लें कि आपके पास दो अशक्त तार नहीं हैं (यानी किसी अन्य विधि से लौटाए गए):

String p1 = "p1"; 
String p2 = "p2";

आप उन्हें वैकल्पिक में लपेटने के लिए मजबूर हैं भले ही आप जानते हों कि वे खाली नहीं हैं।

यह और भी बदतर हो जाता है जब आपको अन्य "मैपेबल" संरचनाओं के साथ रचना करनी होती है, अर्थात। आयर्स :

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

संदर्भ:

तरीकों से अपेक्षा नहीं करनी चाहिए विकल्प के रूप में पैरामीटर, यह लगभग हमेशा एक कोड गंध है जो कॉलर से कैलली तक नियंत्रण प्रवाह के रिसाव का संकेत देता है, यह एक विकल्प की सामग्री की जांच करने के लिए कॉलर की जिम्मेदारी होनी चाहिए।

संदर्भ। https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749


1

मुझे लगता है कि ऐसा इसलिए है क्योंकि आप आमतौर पर डेटा को हेरफेर करने के लिए अपने कार्यों को लिखते हैं, और फिर इसका Optionalउपयोग करके mapऔर इसी तरह के कार्यों के लिए उठाते हैं । यह इसमें डिफ़ॉल्ट Optionalव्यवहार जोड़ता है। बेशक, ऐसे मामले हो सकते हैं, जब अपने स्वयं के कार्य को लिखने के लिए आवश्यक है जो काम करता है Optional


1

मेरा मानना ​​है कि होने का अनुनाद यह है कि आपको पहले यह जांचना होगा कि क्या वैकल्पिक स्वयं अशक्त है या नहीं और फिर मूल्य का मूल्यांकन करने की कोशिश करता है। बहुत सारे अनावश्यक सत्यापन।


2
एक अशक्त वैकल्पिक संदर्भ को पास करना एक प्रोग्रामिंग त्रुटि माना जा सकता है (क्योंकि वैकल्पिक एक "खाली" मान पास करने का एक स्पष्ट तरीका प्रदान करता है ), इसलिए यह जाँच के लायक नहीं है, जब तक कि आप सभी मामलों में अपवादों को फेंकने से बचना चाहते हैं।
पवरगॉन

1

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

आप हमेशा यह बताने के लिए @ नोट का उपयोग कर सकते हैं कि विधि तर्क शून्य हो सकता है। वैकल्पिक का उपयोग करना वास्तव में आपको अपने तरीके के तर्क को अधिक करीने से लिखने में सक्षम नहीं करता है।


5
मुझे खेद है, लेकिन मैं इस तर्क से सहमत नहीं हो सकता "डिजाइन नहीं किया गया था" या "कोई इसके खिलाफ सिफारिश करता है"। एक इंजीनियर हमें विशिष्ट होना चाहिए। Optional का उपयोग करके @Nullable का उपयोग करना बहुत बुरा है, क्योंकि API एक दृष्टिकोण से वैकल्पिक अधिक क्रिया हैं। मैं तर्क के रूप में वैकल्पिक उपयोग करने के खिलाफ कोई अच्छा कारण नहीं देखता (बशर्ते आप विधि अधिभार का उपयोग नहीं करना चाहते)
Kacper86

0

एक और दृष्टिकोण, आप क्या कर सकते हैं

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

एक बार जब आप एक फ़ंक्शन (इस मामले में आपूर्तिकर्ता) का निर्माण कर लेते हैं, तो आप इसे किसी भी अन्य चर के रूप में पास कर पाएंगे और इसका उपयोग करके कॉल कर पाएंगे

calculatedValueSupplier.apply();

यहां यह विचार किया जा रहा है कि क्या आपको वैकल्पिक मूल्य मिला है या नहीं, यह आपके फ़ंक्शन का आंतरिक विवरण होगा और पैरामीटर में नहीं होगा। पैरामीटर के रूप में वैकल्पिक के बारे में सोचते समय कार्य वास्तव में बहुत उपयोगी तकनीक है जो मैंने पाया है।

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


0

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

आपके उदाहरण के लिए, जहां प्रत्येक पैरामीटर वैकल्पिक है, मैं इस तरह से गणना पद्धति को अपनी कक्षा में बदलने का सुझाव दूंगा:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();

0

मुझे पता है कि यह सवाल कठिन तथ्यों के बजाय राय के बारे में अधिक है। लेकिन मैं हाल ही में एक .net डेवलपर से एक जावा एक होने के लिए चला गया, इसलिए मैं केवल हाल ही में वैकल्पिक पार्टी में शामिल हो गया हूं। इसके अलावा, मैं इसे एक टिप्पणी के रूप में बताना चाहूंगा, लेकिन चूंकि मेरा बिंदु स्तर मुझे टिप्पणी करने की अनुमति नहीं देता है, इसलिए मुझे इसके बजाय उत्तर के रूप में रखने के लिए मजबूर किया जाता है।

मैं क्या कर रहा हूं, जिसने मुझे अंगूठे के नियम के रूप में अच्छी तरह से सेवा दी है। रिटर्न प्रकारों के लिए वैकल्पिक का उपयोग करना है, और केवल वैकल्पिक के मापदंडों के रूप में उपयोग करना है, अगर मुझे वैकल्पिक के दोनों मूल्य की आवश्यकता है, और मौसम या नहीं वैकल्पिक का विधि के भीतर एक मूल्य था।

यदि मैं केवल मूल्य के बारे में परवाह करता हूं, तो मैं फोन करने से पहले जाँच करता हूं कि क्या विधि है, अगर मेरे पास किसी प्रकार का लॉगिंग या अलग तर्क है, जो इस बात पर निर्भर करता है कि क्या मूल्य मौजूद है, तो मैं खुशी से वैकल्पिक में पास हो जाऊंगा।


0

ऐसा इसलिए है क्योंकि हमारे पास एपीआई उपयोगकर्ता और एपीआई डेवलपर की अलग-अलग आवश्यकताएं हैं।

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

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


0

सबसे पहले, यदि आप विधि 3 का उपयोग कर रहे हैं, तो आप उन कोड की अंतिम 14 पंक्तियों को इस से बदल सकते हैं:

int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

आपके द्वारा लिखी गई चार विविधताएँ सुविधा विधियाँ हैं। अधिक सुविधाजनक होने पर आपको केवल उनका उपयोग करना चाहिए। यह भी सबसे अच्छा तरीका है। इस तरह, API बहुत स्पष्ट है कि कौन से सदस्य आवश्यक हैं और कौन से नहीं हैं। यदि आप चार तरीके नहीं लिखना चाहते हैं, तो आप चीजों को स्पष्ट कर सकते हैं कि आप अपने मापदंडों को कैसे नाम देते हैं:

public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)

इस तरह, यह स्पष्ट है कि शून्य मानों की अनुमति है।

आपका उपयोग p1.orElse(null)दिखाता है कि वैकल्पिक का उपयोग करते समय हमारा कोड कैसे क्रियाशील हो जाता है, जो इसका कारण है कि मैं इससे बचता हूं। वैकल्पिक को कार्यात्मक प्रोग्रामिंग के लिए लिखा गया था। धाराओं की जरूरत है। आपके तरीकों को संभवतः वैकल्पिक कभी नहीं लौटना चाहिए जब तक कि उन्हें कार्यात्मक प्रोग्रामिंग में उपयोग करना आवश्यक न हो। Optional.flatMap()विधि की तरह विधियाँ हैं , जिसमें एक फ़ंक्शन के संदर्भ की आवश्यकता होती है जो वैकल्पिक लौटाता है। यहाँ इसके हस्ताक्षर हैं:

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

इसलिए यह आमतौर पर एक तरीका है जो वैकल्पिक रिटर्न लिखने का एकमात्र अच्छा कारण है। लेकिन वहां भी, इससे बचा जा सकता है। आप एक ऐसे गेट्टर को पास कर सकते हैं जो फ्लैटपाइप () जैसी विधि के लिए वैकल्पिक नहीं लौटाता है, इसे दूसरी विधि में लपेटकर जो फ़ंक्शन को सही प्रकार में परिवर्तित करता है। आवरण विधि इस प्रकार दिखती है:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

तो मान लीजिए कि आपके पास एक गटर है: String getName()

आप इसे इस तरह से फ्लैटपाइप पर नहीं भेज सकते:

opt.flatMap(Widget::getName) // Won't work!

लेकिन आप इसे इस तरह से पास कर सकते हैं:

opt.flatMap(optFun(Widget::getName)) // Works great!

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

ब्रायन गोएट्ज़ ने जब यह कहा तो यह सबसे अच्छा था:

वैकल्पिक कारण जावा में जोड़ा गया था क्योंकि यह है:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

इस से क्लीनर है:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

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