क्या जावा 8 पाने वालों को वैकल्पिक प्रकार लौटाना चाहिए?


288

Optional जावा 8 में पेश किया गया प्रकार कई डेवलपर्स के लिए एक नई बात है।

क्या एक गेट्टर विधि Optional<Foo>क्लासिक के स्थान पर Fooएक अच्छा अभ्यास है? मान लें कि मान हो सकता है null


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

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

1
@NotNullएनोटेशन पर चर्चा भी देखें : stackoverflow.com/q/4963300/873282
koppor

जवाबों:


516

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

उदाहरण के लिए, आपको संभवतः इसका उपयोग कभी ऐसी चीज़ के लिए नहीं करना चाहिए जो परिणामों की एक सरणी, या परिणामों की सूची लौटाती है; इसके बजाय एक खाली सरणी या सूची लौटाएं। आपको लगभग कभी भी किसी चीज़ के क्षेत्र या विधि पैरामीटर के रूप में इसका उपयोग नहीं करना चाहिए।

मुझे लगता है कि नियमित रूप से इसे गेटर्स के लिए रिटर्न वैल्यू के रूप में उपयोग करना निश्चित रूप से अति-उपयोग होगा।

वैकल्पिक के साथ कुछ भी गलत नहीं है कि इसे टाला जाना चाहिए, यह सिर्फ वही नहीं है जो कई लोग चाहते हैं, और तदनुसार हम उत्साही अति-उपयोग के जोखिम के बारे में काफी चिंतित थे।

(सार्वजनिक सेवा घोषणा: कभी फोन Optional.getजब तक आप साबित कर सकते हैं यह शून्य कभी नहीं होगा, बल्कि तरह सुरक्षित एक विधि का उपयोग orElseया ifPresent। पीछे मुड़कर देखें तो हमें बुलाया जाना चाहिए था getकी तरह कुछ getOrElseThrowNoSuchElementExceptionया कुछ और है कि यह अब तक स्पष्ट यह था कि एक बेहद खतरनाक विधि बनाया कि Optionalपहली जगह में पूरे उद्देश्य को कम कर दिया । सबक सीखा। (अद्यतन: जावा 10 है Optional.orElseThrow(), जो शब्दार्थ के बराबर है get(), लेकिन जिसका नाम अधिक उपयुक्त है।)


24
(पिछले भाग के बारे में) ... और जब हम आश्वस्त होते हैं, कि मूल्य कभी भी nullहम उपयोग नहीं कर सकते हैं orElseThrow(AssertionError::new), अहम या orElseThrow(NullPointerException::new)...
होल्गर

25
आप अलग क्या किया है, तो आपका इरादा होता था शायद या कुछ प्रकार एक सामान्य प्रयोजन लागू करने के लिए किया गया? क्या कोई ऐसा तरीका है जिसमें वैकल्पिक एक के लिए बिल फिट नहीं करता है, या यह सिर्फ इतना है कि एक नए एपीआई पर वैकल्पिक को पेश करने से यह गैर-जावा-ईश बन जाएगा?
डेविड मोल्स

22
"सामान्य-प्रयोजन प्रकार" से मेरा अभिप्राय भाषा के प्रकार प्रणाली में इसका निर्माण करना था, न कि इसे पुस्तकालय की कक्षा प्रदान करने के बजाय जो इसे अनुमानित करता है। (कुछ भाषाओं में T के लिए प्रकार हैं! (T या null) और T (non-nullable T)) वैकल्पिक सिर्फ एक वर्ग है; जैसा कि हम भाषा समर्थन के साथ कर सकते हैं, हम फू और वैकल्पिक <फू> के बीच निहित रूपांतरण नहीं कर सकते हैं।
ब्रायन गोएटज

11
मैं थोड़ी देर के लिए आश्चर्यचकित हूं कि क्यों जावा दुनिया में जोर वैकल्पिक पर था, और बेहतर स्थैतिक विश्लेषण नहीं। वैकल्पिक के कुछ फायदे हैं, लेकिन जो बड़ा लाभ है nullवह है पश्चगामी संगतता; Map::getएक अशक्त V, नहीं Optional<V>, और यह कभी नहीं बदलने वाला है। यह आसानी से एनोटेट किया जा सकता था @Nullable, हालांकि। अब हमारे पास अभाव-मूल्य को व्यक्त करने के दो तरीके हैं, साथ ही वास्तव में स्थिर विश्लेषण प्राप्त करने के लिए कम प्रोत्साहन, जो कि होने के लिए एक बदतर स्थिति की तरह लगता है।
yshavit

21
मुझे नहीं पता था कि किसी संपत्ति के लिए रिटर्न वैल्यू के रूप में इसका उपयोग तब तक नहीं किया जा सकता था, जब तक कि मैं इस जवाब को स्टैकवर्वरफ्लो पर यहां नहीं पढ़ता। वास्तव में, मुझे विश्वास था कि यह ओरेकल की वेबसाइट पर इस लेख को पढ़ने के बाद बिल्कुल ठीक था, जिसका उद्देश्य था: oracle.com/technetwork/articles/java/… यह स्पष्ट करने के लिए धन्यवाद
जेसन थॉम्पसन

73

अपना खुद का थोड़ा सा शोध करने के बाद, मैं कई चीजों को लेकर आया हूं जो यह सुझाव दे सकती हैं कि यह कब उचित है। ओरेकल लेख से सबसे अधिक आधिकारिक होने के बाद निम्नलिखित उद्धरण:

"यह ध्यान रखना महत्वपूर्ण है कि वैकल्पिक वर्ग का इरादा हर एक अशक्त संदर्भ को प्रतिस्थापित करना नहीं है । इसके बजाय, इसका उद्देश्य अधिक-समझ वाले एपीआई को डिजाइन करने में मदद करना है ताकि किसी विधि के हस्ताक्षर को पढ़कर आप यह बता सकें कि क्या आप एक वैकल्पिक मूल्य की उम्मीद कर सकते हैं। यह आपको मूल्य की अनुपस्थिति से निपटने के लिए एक वैकल्पिक रूप से सक्रिय करने के लिए मजबूर करता है। " - अशक्त सूचक अपवादों से थक गए? जावा एसई 8 के वैकल्पिक का उपयोग करने पर विचार करें!

मुझे जावा 8 वैकल्पिक का यह अंश भी मिला : इसका उपयोग कैसे करें

"इन संदर्भों में वैकल्पिक का उपयोग करने का मतलब नहीं है, क्योंकि यह हमें कुछ भी नहीं खरीदेगा:

  • डोमेन मॉडल परत में (क्रमिक नहीं)
  • डीटीओ में (समान कारण)
  • तरीकों के इनपुट मापदंडों में
  • निर्माता मापदंडों में "

जो कुछ मान्य बिंदुओं को बढ़ाता हुआ भी प्रतीत होता है।

मैं किसी भी नकारात्मक धारणा या लाल झंडे को खोजने में सक्षम नहीं था जो यह सुझाव देता था कि Optionalइसे टाला जाना चाहिए। मुझे लगता है कि सामान्य विचार यह है, यदि यह उपयोगी है या आपके एपीआई की उपयोगिता में सुधार करता है, तो इसका उपयोग करें।


11
stackoverflow.com/questions/25693309 - ऐसा लगता है कि जैक्सन पहले से ही इसका समर्थन करता है, इसलिए "नहीं धारावाहिक" अब एक वैध कारण के रूप में गुजरता है :)
Vlasec

1
मेरा सुझाव है कि आपके जवाब पते Optionalके तरीकों के इनपुट मापदंडों का उपयोग करके (अधिक विशेष रूप से निर्माणकर्ता) "हमें कुछ भी नहीं खरीदेंगे"। dolszewski.com/java/java-8-optional-use-cases में एक अच्छी व्याख्या है।
गिली

मैंने पाया है कि वैकल्पिक परिणामों पर अंकुश लगाने के लिए जिन्हें अन्य एपीआई में बदलने की आवश्यकता है, एक वैकल्पिक पैरामीटर की आवश्यकता है। परिणाम एक काफी समझदार एपीआई है। देखें stackoverflow.com/a/31923105/105870
बुतपरस्त

1
लिंक टूट गया है। क्या आप उत्तर को अपडेट कर सकते हैं?
सॉफ्टनर

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

20

मैं सामान्य तौर पर रिटर्न मानों के लिए वैकल्पिक प्रकार का उपयोग करने के लिए एक अच्छा विचार कहूंगा जो अशक्त हो सकता है। हालाँकि, फ्रेमवर्क के लिए wrt मुझे लगता है कि वैकल्पिक प्रकारों के साथ शास्त्रीय गेटर्स को बदलने पर फ्रेमवर्क (जैसे, हाइबरनेट) के साथ काम करते समय बहुत परेशानी होगी, जो गेटर्स और सेटर के लिए कोडिंग सम्मेलनों पर भरोसा करते हैं।


14
यह सलाह मेरे द्वारा "हम अति उत्साही के जोखिम के बारे में चिंतित हैं" के बारे में बिल्कुल उसी तरह की है जैसा कि stackoverflow.com/a/26328555/3553087 में है
ब्रायन गोएट्ज़

13

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

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;

मेरा कहना यह है कि वैकल्पिक को कार्यात्मक प्रोग्रामिंग का समर्थन करने के लिए लिखा गया था , जिसे उसी समय जावा में जोड़ा गया था। (उदाहरण ब्रायन गोएत्ज़ द्वारा एक ब्लॉग के सौजन्य से आया है । एक बेहतर उदाहरण orElse()विधि का उपयोग कर सकता है , क्योंकि यह कोड वैसे भी एक अपवाद को फेंक देगा, लेकिन आपको चित्र मिल जाएगा।)

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

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

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

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

एपीआई दोष के लिए वैकल्पिक एक बहुत बुरा समाधान है क्योंकि क) वे बहुत ही क्रियात्मक हैं, और बी) वे पहली बार में उस समस्या को हल करने के लिए कभी नहीं थे।

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

इससे सब कुछ बदल जाता है।

अपने गेटर्स के लिए, वैकल्पिक का उपयोग न करें। और अपनी कक्षाओं को डिजाइन करने का प्रयास करें ताकि कोई भी सदस्य संभवतः अशक्त न हो सके। और हो सकता है कि अपने प्रोजेक्ट में Nullness Checker को जोड़ने और अपने गेटर्स और सेटर पैरामीटर @Nullable की घोषणा करें यदि उन्हें इसकी आवश्यकता है। मैंने केवल नई परियोजनाओं के साथ ऐसा किया है। यह संभवत: अशक्त के लिए बहुत सारे सुपरफ्लस परीक्षणों के साथ लिखी गई मौजूदा परियोजनाओं में बहुत अधिक चेतावनी देता है, इसलिए यह पुराना करना कठिन हो सकता है। लेकिन यह बहुत सारे कीड़े भी पकड़ेगा। मुझे यह पसंद है। मेरा कोड इसकी वजह से बहुत साफ और अधिक विश्वसनीय है।

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

मूल पद के लिए परिशिष्ट (संस्करण 2)

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

ध्यान रखें कि कार्यात्मक प्रोग्रामिंग के लिए तैयार किए गए तरीके, और जिसके लिए फ़ंक्शन संदर्भ की आवश्यकता होती है, (और) को दो रूपों में लिखा जाना चाहिए, जिनमें से एक वैकल्पिक का उपयोग करता है। उदाहरण के लिए, Optional.map()और Optional.flatMap()दोनों फ़ंक्शन संदर्भ लेते हैं। पहला एक साधारण गटर का संदर्भ लेता है, और दूसरा वह लेता है जो वैकल्पिक लौटाता है। तो आप किसी को भी एक वैकल्पिक रिटर्न देकर एहसान नहीं कर रहे हैं जहाँ मूल्य शून्य नहीं हो सकता है।

यह सब कहने के बाद, मैं अभी भी अशक्तता से निपटने के लिए Nullness Checker द्वारा उपयोग किए जाने वाले दृष्टिकोण को देखता हूं , क्योंकि वे NullPointerException को रन टाइम बग से संकलित करने के लिए समय त्रुटियों को संकलित करते हैं।


2
यह उत्तर मुझे सबसे उपयुक्त लगता है। वैकल्पिक केवल जावा 8 में जोड़ा गया था जब धाराओं को जोड़ा गया था। और केवल स्ट्रीम फ़ंक्शंस वैकल्पिक रूप से लौटते हैं जहाँ तक मैंने देखा है।
अर्चित जूल

1
मुझे लगता है कि यह सबसे अच्छा जवाब में से एक है। लोग जानते हैं कि वैकल्पिक क्या है और यह कैसे काम करता है। लेकिन सबसे अधिक भ्रमित करने वाला हिस्सा है / इसका उपयोग कहां करना है और इसका उपयोग कैसे करना है। इसे पढ़ने से यह बहुत सारे संदेह को दूर करता है।
ParagFlume

3

यदि आप आधुनिक धारावाहिकों और अन्य रूपरेखाओं का उपयोग कर रहे हैं जो समझते हैं Optionalतो मैंने पाया है कि ये दिशा-निर्देशEntity सेम और डोमेन परतों को लिखते समय अच्छी तरह से काम करते हैं:

  1. क्रमबद्धता परत (आमतौर पर एक डीबी) एक की अनुमति देता है nullकॉलम में एक सेल के लिए मूल्य BARतालिका में FOO, तो गेटर Foo.getBar()लौट सकते हैं Optionalडेवलपर के लिए यह दर्शाता है कि इस मूल्य यथोचित अशक्त होने की उम्मीद की जा सकती है और वे इस संभाल चाहिए। अगर DB गारंटी देता है कि मूल्य शून्य नहीं होगा, तो गेटटर को इसे नहीं लपेटना चाहिए Optional
  2. Foo.barहोना चाहिए privateऔर नहीं होना चाहिए OptionalOptionalअगर ऐसा है तो इसके होने का वास्तव में कोई कारण नहीं है private
  3. सेटर Foo.setBar(String bar)को किस प्रकार का लेना चाहिए barऔर क्या नहीं Optional । यदि किसी nullतर्क का उपयोग करना ठीक है, तो इसे JavaDoc टिप्पणी में बताएं। यदि यह nullएक IllegalArgumentExceptionया कुछ उपयुक्त व्यावसायिक तर्क का उपयोग करने के लिए ठीक नहीं है, तो IMHO, अधिक उपयुक्त है।
  4. कंस्ट्रक्टर्स को Optionalतर्कों की आवश्यकता नहीं है (बिंदु 3 के समान कारणों के लिए)। आम तौर पर मैं केवल कंस्ट्रक्टर में तर्क शामिल करता हूं जो क्रमबद्धता डेटाबेस में गैर-शून्य होना चाहिए।

उपरोक्त को और अधिक कुशल बनाने के लिए, आप गेटवे बनाने के लिए अपने आईडीई टेम्प्लेट को संपादित कर सकते हैं toString(), equals(Obj o)आदि के लिए संबंधित टेम्प्लेट या उन लोगों के लिए सीधे फ़ील्ड का उपयोग करें (अधिकांश आईडीई जनरेटर पहले से ही नल से निपटते हैं)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.