पारंपरिक null पॉइंटर चेक के बजाय Java 8+ में वैकल्पिक का उपयोग क्यों करें?


110

हम हाल ही में जावा 8 में चले गए हैं। अब, मैं देख रहा हूं कि Optionalवस्तुओं के साथ बाढ़ आ गई है।

जावा 8 (स्टाइल 1) से पहले

Employee employee = employeeServive.getEmployee();

if(employee!=null){
    System.out.println(employee.getId());
}

जावा 8 (स्टाइल 2) के बाद

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employee = employeeOptional.get();
    System.out.println(employee.getId());
}

Optional<Employee> employeeOptional = employeeService.getEmployee();जब सेवा स्वयं वैकल्पिक लौटाती है तो मुझे कोई अतिरिक्त मूल्य नहीं दिखता है :

एक जावा 6 पृष्ठभूमि से आते हुए, मैं स्टाइल 1 को अधिक स्पष्ट और कोड की कम लाइनों के साथ देखता हूं। क्या कोई वास्तविक लाभ है जो मुझे यहाँ याद आ रहा है?

ब्लॉग पर सभी उत्तरों और आगे के शोध से समझ से समेकित



28
नीरस employeeOptional.isPresent()लगता है पूरी तरह से विकल्प के प्रकार की याद आ रही है। @ माइकपार्ट्रिज की टिप्पणी के अनुसार, यह अनुशंसित या मुहावरेदार नहीं है। स्पष्ट रूप से जांचना कि क्या एक विकल्प प्रकार कुछ है या कुछ भी नहीं-नहीं है। आप बस उन्हें mapया flatMapउन पर माना जाता है ।
एंड्रेस एफ।

7
ओरेकल में जावा लैंग्वेज आर्किटेक्ट ब्रायन गोएत्ज़ द्वारा एक स्टैक ओवरफ्लो उत्तरOptional देखें ।
बेसिल बोर्के

6
ऐसा तब होता है जब आप अपने मस्तिष्क को "सर्वोत्तम प्रथाओं" के नाम से बंद कर देते हैं।
इम्बिसिस

9
यह वैकल्पिक के काफी खराब उपयोग है लेकिन यहां तक ​​कि लाभ भी है। नल के साथ सब कुछ शून्य हो सकता है, इसलिए आपको लगातार जाँच करने की आवश्यकता है, वैकल्पिक कॉल यह बताता है कि यह विशेष चर गायब होने की संभावना है, सुनिश्चित करें कि आप इसके लिए जांच करें
रिचर्ड टिंगल

जवाबों:


108

स्टाइल 2 पूर्ण लाभ देखने के लिए जावा 8 पर्याप्त नहीं है। आप बिल्कुल नहीं चाहते हैं if ... useओरेकल के उदाहरण देखें । उनकी सलाह लेते हुए, हम प्राप्त करते हैं:

शैली ३

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

या अधिक लंबा स्निपेट

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));

19
वास्तव में हम isPresent के सबसे अधिक उपयोग पर प्रतिबंध लगाते हैं (कोड समीक्षा द्वारा पकड़ा गया)
jk।

10
@jk। वास्तव में, अगर कोई ओवरलोड void ifPresent(Consumer<T>, Runnable)होता , तो मैं isPresentget
कैलथ

10
@ कैलेथ: आपको जावा 9 में दिलचस्पी हो सकती है ifPresentOrElse(Consumer<? super T>, Runnable)
व्रचगिन

9
मुझे जावा 6 की तुलना में इस जावा 8 शैली में एक दोष लगता है: यह कोड कवरेज टूल को मूर्ख बनाता है। यदि स्टेटमेंट के साथ, यह दिखाता है कि आप ब्रांच से चूक गए हैं, तो यहां नहीं।
थियरी

14
@ एरॉन "कोड की पठनीयता को काफी कम कर देता है"। क्या हिस्सा वास्तव में पठनीयता को कम करता है? ऐसा लगता है जैसे "मैंने हमेशा इसे अलग तरीके से किया है और मैं उद्देश्यपूर्ण तर्क की तुलना में अपनी आदतों को बदलना नहीं चाहता"।
वू

47

यदि आप Optionalपुराने API के बीच "संगतता" परत के रूप में उपयोग कर रहे हैं जो अभी भी वापस आ सकता है null, तो यह नवीनतम चरण में (गैर-खाली) वैकल्पिक बनाने के लिए सहायक हो सकता है जो आपको यकीन है कि आपके पास कुछ है। जैसे, आपने कहाँ लिखा है:

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

मैं पसंद करूँगा:

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

मुद्दा यह है कि आप जानते हैं कि एक गैर-अशक्त कर्मचारी सेवा है , इसलिए आप इसे एक Optionalसाथ लपेट सकते हैं Optional.of()। फिर, जब आप getEmployee()उस पर कॉल करते हैं, तो आपको कर्मचारी मिल सकता है या नहीं। उस कर्मचारी के पास (या, संभवतः, हो सकता है) एक आईडी न हो। फिर, यदि आप एक आईडी के साथ समाप्त हो गए हैं, तो आप इसे प्रिंट करना चाहते हैं।

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


4
(...).ifPresent(aMethod)ओवर का उपयोग करने का क्या फायदा है if((...).isPresent()) { aMethod(...); }? यह लगभग एक ही ऑपरेशन करने के लिए बस एक और वाक्यविन्यास प्रतीत होता है।
रुस्लान

2
@Ruslan एक विशिष्ट कॉल के लिए एक विशेष सिंटैक्स का उपयोग क्यों करें जब आपके पास पहले से ही एक सामान्य समाधान है जो सभी मामलों के लिए समान रूप से अच्छी तरह से काम करता है? हम मानचित्र और सह के समान विचार यहाँ भी लागू कर रहे हैं।
वू

1
@Ruslan ... एक API खाली संग्रह या सरणियों के बजाय नल देता है। उदाहरण के लिए, आप कुछ ऐसा कर सकते हैं (एक पैरेंट क्लास संभालने के लिए जो getChildren () बच्चों का एक सेट लौटाता है, या यदि कोई बच्चे नहीं हैं तो शून्य): Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet); अब मैं childrenसमान रूप से प्रक्रिया कर सकता हूं , जैसे children.forEach(relative::sendGift);,। उन लोगों को भी जोड़ा जा सकता है Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);:। अशक्त जाँच के सभी बॉयलरप्लेट, आदि ...
जोशुआ टेलर

1
@Ruslan ... मानक पुस्तकालय में आजमाया हुआ और सही कोड है। यह एक प्रोग्रामर के रूप में कोड की मात्रा को कम करता है, जो मुझे लिखना है, परीक्षण करना है, डिबग करना है आदि
यहोशू टेलर

1
खुशी है कि मैं स्विफ्ट का उपयोग कर रहा हूं। यदि कर्मचारी = employeeService.employee {प्रिंट (कर्मचारी.ड); }
gnasher729

23

Optionalएक एकल मान होने में कोई अतिरिक्त मूल्य नहीं है । जैसा कि आपने देखा है, कि सिर्फ nullउपस्थिति के लिए चेक के साथ चेक की जगह लेता है ।

है बड़ा एक होने में जोड़ा मूल्य Collectionइस तरह की चीजों की। पुरानी शैली के निम्न-स्तर के छोरों को बदलने के लिए शुरू की गई सभी स्ट्रीमिंग विधियां Optionalचीजों को समझती हैं और उनके बारे में सही काम करती हैं (उन्हें संसाधित नहीं करना या अंत में किसी अन्य को वापस करना Optional)। यदि आप अलग-अलग मदों (और सभी यथार्थवादी कार्यक्रमों के 99% करते हैं) की तुलना में अधिक बार एन वस्तुओं से निपटते हैं , तो वैकल्पिक रूप से मौजूद चीजों के लिए अंतर्निहित समर्थन का होना एक बड़ा सुधार है। आदर्श मामले में आपको कभी भी जाँच null या उपस्थिति के लिए नहीं होना चाहिए ।


24
संग्रह / धाराओं के अलावा, एपीआई को अधिक स्व-दस्तावेजीकरण, उदाहरण के लिए Employee getEmployee()बनाम बनाने के लिए एक वैकल्पिक भी बहुत अच्छा है Optional<Employee> getEmployee()। जबकि तकनीकी रूप से दोनों वापस आ सकते हैं null, उनमें से एक के लिए परेशानी पैदा करने की संभावना अधिक है।
आमोन

16
एकल मान के वैकल्पिक में बहुत अधिक मूल्य है। .map()जिस तरह से सभी के लिए अशक्त की जाँच करने के लिए बिना सक्षम होने के नाते महान है। जैसे Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);,।
जोशुआ टेलर

9
मैं बिना किसी अतिरिक्त मूल्य के आपकी प्रारंभिक टिप्पणी से असहमत हूं। वैकल्पिक आपके कोड के संदर्भ प्रदान करता है। एक त्वरित नज़र आपको किसी फ़ंक्शन की शुद्धता के बारे में बता सकती है और सभी मामलों को कवर किया जाता है। जबकि अशक्त जाँच के साथ, आपको प्रत्येक एकल विधि पर हर एक संदर्भ प्रकार की जाँच करनी चाहिए? एक समान मामला कक्षाओं और सार्वजनिक / निजी संशोधक पर लागू किया जा सकता है; वे आपके कोड में शून्य मान जोड़ते हैं, है ना?
ArTs

3
@JoshuaTaylor ईमानदारी से, उस अभिव्यक्ति की तुलना में , मैं पुराने जमाने के चेक को पसंद करता हूं null
किलियन फोथ

2
एकल मानों के साथ भी वैकल्पिक का एक और बड़ा फायदा यह है कि जब आप करना चाहिए तो आप अशक्त के लिए जांच करना नहीं भूल सकते हैं। अन्यथा चेक को भूलना और NullPointerException के साथ समाप्त करना बहुत आसान है ...
शॉन बर्टन

17

जब तक आप के लिए Optionalसिर्फ एक फैंसी एपीआई की तरह उपयोग करते हैं isNotNull(), तो हाँ, आप बस के लिए जाँच के साथ कोई अंतर नहीं मिलेगा null

चीजें आप का उपयोग नहीं करना चाहिए Optionalके लिए

Optionalकिसी मान की उपस्थिति के लिए जाँच करने के लिए उपयोग करना खराब कोड ™ है:

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

चीजें आप का उपयोग करना चाहिए Optionalके लिए

शून्य-वापसी एपीआई विधियों का उपयोग करते समय आवश्यक होने पर एक का उत्पादन करके, मौजूद मूल्य से बचें:

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

या, यदि एक नया कर्मचारी बनाना बहुत महंगा है:

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

इसके अलावा, हर किसी को इस बात से अवगत कराना कि आपके तरीके एक मान नहीं लौटा सकते (यदि, किसी भी कारण से, आप ऊपर दिए गए मानों को वापस नहीं कर सकते हैं):

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

और अंत में, सभी स्ट्रीम जैसी विधियां हैं जो पहले से ही अन्य उत्तरों में बताई गई हैं, हालांकि वे वास्तव में आप उपयोग करने का निर्णय नहीं ले रहे हैं, Optionalलेकिन Optionalकिसी और द्वारा प्रदान किए गए उपयोग का उपयोग कर रहे हैं ।


1
@ कटपिप तथ्य। हमने / r / java पर यह बहस की, और मैंने कहा : «मैं Optionalएक चूक के अवसर के रूप में देखता हूं । "यहाँ, इस नई Optionalचीज़ को मैंने बनाया है ताकि आप अशक्त मूल्यों की जाँच करने के लिए मजबूर हों। ओह, और वैसे भी ... मैंने इसमें एक get()विधि जोड़ी है इसलिए आपको वास्तव में किसी चीज़ की जाँच करने की ज़रूरत नहीं है;);)" । (...) Optional"इस दृष्टि से नीली" नीयन संकेत के रूप में अच्छा है, लेकिन इसकी get()विधि का नंगे अस्तित्व » । लेकिन ओपी उपयोग करने के लिए कारण पूछ रहा था Optional, न कि इसके खिलाफ या डिजाइन की खामियों के कारण।
Walen

1
Employee employee = Optional.ofNullable(employeeService.getEmployee());आपके तीसरे स्निपेट में, क्या प्रकार नहीं होना चाहिए Optional<Employee>?
चार्ली हार्डिंग

2
@immibis किस तरह से अपंग हुआ? उपयोग करने का मुख्य कारण यह getहै कि यदि आपको कुछ विरासत कोड के साथ बातचीत करनी है। मैं कभी भी उपयोग करने के लिए नए कोड में कई वैध कारणों के बारे में नहीं सोच सकता get। यह कहा कि जब विरासत कोड के साथ बातचीत करते हैं तो अक्सर कोई विकल्प नहीं होता है, लेकिन फिर भी एक बिंदु होता है, getजिससे शुरुआती लोगों का अस्तित्व खराब कोड लिखने का कारण बनता है।
वू

1
@ आईमिसिस का मैंने Mapएक बार भी उल्लेख नहीं किया है और मुझे इस तरह के कठोर गैर-पिछड़े संगत बदलाव के बारे में किसी को भी जानकारी नहीं है, इसलिए सुनिश्चित करें कि आप जानबूझकर खराब एपीआई को डिजाइन कर सकते हैं Optional- यह सुनिश्चित नहीं है कि क्या साबित होता है। एक हालांकि Optionalकुछ tryGetविधि के लिए समझ में आता है ।
वू

2
@Voo Mapएक ऐसा उदाहरण है जिससे हर कोई परिचित है। बेशक वे Map.getपीछे की संगतता के कारण एक वैकल्पिक रिटर्न नहीं कर सकते हैं , लेकिन आप आसानी से एक और मानचित्र जैसी कक्षा की कल्पना कर सकते हैं जिसमें ऐसी संगतता बाधाएं नहीं होंगी। कहते हैं class UserRepository {Optional<User> getUserDetails(int userID) {...}}। अब आप उपयोगकर्ता के विवरण में लॉग इन करना चाहते हैं, और आप जानते हैं कि वे मौजूद हैं क्योंकि वे लॉग इन करने में कामयाब रहे और आपका सिस्टम उपयोगकर्ताओं को कभी नहीं हटाता है। तो आप करते हैं theUserRepository.getUserDetails(loggedInUserID).get()
इमिबिज़

12

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

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());

8
यह मुझे चकित करता है कि वैकल्पिक के साथ सभी निफ्टी कार्यात्मक सामान, हालांकि वास्तव में अच्छा है, इस बिंदु से अधिक महत्वपूर्ण माना जाता है। Java 8 से पहले, आपको यह जानने के लिए Javadoc के माध्यम से खुदाई करनी थी कि क्या आपको कॉल से प्रतिक्रिया को शून्य करना है। जावा 8 पोस्ट करें, आप रिटर्न प्रकार से जानते हैं कि क्या आपको "कुछ नहीं" वापस मिल सकता है या नहीं।
बुहब

3
खैर, "पुराना कोड" समस्या है जहां चीजें अभी भी अशक्त हो सकती हैं ... लेकिन हां, हस्ताक्षर से बताने में सक्षम होना महान है।
हाकॉन लोट्टविट

5
हाँ, यह निश्चित रूप से उन भाषाओं में बेहतर काम करता है जहाँ वैकल्पिक <T> यह व्यक्त करने का एकमात्र तरीका है कि कोई मान गायब हो सकता है, अर्थात null ptr के बिना भाषाएँ।
dureuill

8

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

इसने आपको पूरी तरह से सुपाठ्य कोड लिखने का नेतृत्व किया है जो पूरी तरह से उचित प्रतीत होता है, लेकिन आपको विले ट्विन प्रलोभनों द्वारा फुसलाया गया जो कि मिलता है और isPresent है।

बेशक सवाल जल्दी बन जाता है "क्यों isPresent और वहाँ भी मिलता है?"

एक चीज जो यहां बहुत से लोगों को याद आती है, वह है Present () यह है कि यह कोई ऐसी चीज नहीं है जो नए कोड के लिए लोगों द्वारा पूरी तरह से बोर्ड पर लिखी जाती है कि कैसे गोश-डार उपयोगी लैंबडास हैं, और जो कार्यात्मक चीज को पसंद करते हैं।

हालांकि, यह हमें कुछ (दो) अच्छे, महान, दमकदार (?) लाभ देता है:

  • यह नई विशेषताओं का उपयोग करने के लिए विरासत कोड के संक्रमण को आसान बनाता है।
  • यह वैकल्पिक के सीखने की अवस्था को आसान बनाता है।

पहले वाला बल्कि सरल है।

कल्पना कीजिए कि आपके पास एक एपीआई है जो इस तरह दिखता है:

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

और आपके पास इस तरह का उपयोग करके एक विरासत वर्ग था (रिक्त स्थान भरें):

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

सुनिश्चित करने के लिए एक आकस्मिक उदाहरण। लेकिन यहाँ मेरे साथ सहन करो।

Java 8 अब लॉन्च हो गया है और हम इसमें सवार होने के लिए हाथ-पांव मार रहे हैं। इसलिए हमारे द्वारा की जाने वाली चीजों में से एक यह है कि हम अपने पुराने इंटरफ़ेस को किसी ऐसी चीज़ से बदलना चाहते हैं जो ऑप्शनल लौटे। क्यों? क्योंकि जैसा कि किसी और ने पहले ही स्पष्ट रूप से उल्लेख किया है: यह अनुमान लगाता है कि क्या कुछ शून्य हो सकता है या नहीं यह पहले ही दूसरों द्वारा इंगित किया जा चुका है। लेकिन अब हमें एक समस्या है। कल्पना कीजिए कि हमारे पास (एक निर्दोष विधि पर alt + F7 हिट करते समय मुझे माफ करना), 46 स्थानों पर जहां इस पद्धति को अच्छी तरह से परीक्षण किए गए विरासत कोड में कहा जाता है जो अन्यथा एक उत्कृष्ट काम करता है। अब आपको इन सभी को अपडेट करना है।

यह वह जगह है जहाँ पर चमकता है।

क्योंकि अब: स्निकर्स lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {फेंकिए नया NoSuchSnickersException (); } और {कंज्यूमर.गिवडायबिटीज (लास्टसनिकर्स); }

हो जाता है:

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

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

ध्यान दें कि इसका मतलब यह है कि आपने जिस तरह से किया है वह अनिवार्य रूप से विरासत में कोड के साथ सौदा करने का एक तरीका है बिना महंगा रीराइट किए। तो नए कोड के बारे में क्या?

ठीक है, आपके मामले में, जहाँ आप केवल कुछ प्रिंट करना चाहते हैं, आप बस करेंगे:

। SnickersCounter.lastConsumedSnickers () ifPresent (System.out :: println);

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

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

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

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

बेशक, यह दिखने में अच्छा लगता है:

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

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

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


1
यहां समस्या यह है कि आप इसे ध्वनि बनाने की कोशिश करते हैं जैसे कि ये कार्यात्मक संस्करण पुराने संस्करणों की तरह ही पठनीय हैं, एक मामले में यहां तक ​​कि एक उदाहरण "बिल्कुल स्पष्ट है।" बात वह नहीं है। यह उदाहरण स्पष्ट था, लेकिन पूरी तरह से ऐसा नहीं है , क्योंकि इसके लिए अधिक विचार की आवश्यकता है। map(x).ifPresent(f)बस एक ही तरह की सहज ज्ञान युक्त भावना नहीं if(o != null) f(x)करता है। ifसंस्करण पूरी तरह से स्पष्ट है। यह एक लोकप्रिय बैंड-वैगन पर कूदने वाले लोगों का एक और मामला है, * वास्तविक प्रगति का मामला नहीं
आरोन

1
ध्यान दें कि मैं यह नहीं कह रहा हूं कि कार्यात्मक प्रोग्रामिंग या के उपयोग में शून्य लाभ है Optional। मैं केवल इस विशिष्ट मामले में वैकल्पिक के उपयोग का उल्लेख कर रहा था, और अधिक-तो पठनीयता के लिए, क्योंकि कई लोग कार्य कर रहे हैं जैसे कि यह प्रारूप समान या उससे अधिक पठनीय है if
हारून

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

@ ऐरन - स्पष्टता वैकल्पिक प्रकार की बात नहीं है। मुझे व्यक्तिगत रूप से लगता है कि वे स्पष्टता में कमी नहीं करते हैं , और कुछ मामले हैं (इस मामले में नहीं) जहां वे इसे बढ़ाते हैं, लेकिन महत्वपूर्ण लाभ यह है कि (जब तक आप उपयोग करने से बचते हैं isPresentऔर getजब तक वास्तविक रूप से संभव है) सुरक्षा में सुधार । यह गलत कोड लिखना कठिन है जो Optional<Whatever>एक अशक्त का उपयोग करने के बजाय यदि कोई मान अनुपस्थित है, तो मूल्य की अनुपस्थिति की जांच करने में विफल रहता है Whatever
जूल्स

यहां तक ​​कि अगर आप तुरंत मानों को बाहर निकालते हैं, तो जब तक आप उपयोग नहीं करते हैंget , तब आपको यह चुनने के लिए मजबूर किया जाता है कि लापता मूल्य होने पर क्या करना है: आप या तो एक डिफ़ॉल्ट की आपूर्ति करने के लिए orElse()(या orElseGet()) का उपयोग करेंगे , या orElseThrow()एक चुना फेंकने के लिए। अपवाद जो समस्या की प्रकृति का दस्तावेज है , जो एक सामान्य एनपीई से बेहतर है जो तब तक अर्थहीन है जब तक आप स्टैक ट्रेस में खोद नहीं लेते।
जूल्स

0

मुझे शैली 2 में लाभ नहीं दिखता है। आपको अभी भी यह महसूस करने की आवश्यकता है कि आपको अशक्त जांच की आवश्यकता है और अब यह अधिक बड़ा है, इस प्रकार कम पठनीय है।

बेहतर शैली होगी, अगर कर्मचारी Servive.getEmployee () वैकल्पिक लौटेगा और फिर कोड बन जाएगा:

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

इस तरह से आप callemployeeServive.getEmployee ()। GetId () नहीं कर सकते हैं और आपको लगता है कि आपको किसी तरह वैकल्पिक घाटी को संभालना चाहिए। और अगर आपके पूरे कोडबेस तरीके कभी भी अशक्त नहीं लौटते हैं (या इस नियम के बारे में आपकी टीम के अपवादों से अच्छी तरह वाकिफ नहीं हैं), तो यह NPE के खिलाफ सुरक्षा बढ़ाएगा।


12
वैकल्पिक आपके द्वारा उपयोग किए जाने वाले क्षण के लिए एक व्यर्थ जटिलता बन जाता है isPresent()। हम वैकल्पिक का उपयोग करते हैं ताकि हमें परीक्षण न करना पड़े।
कैंडिड_ऑरेंज

2
जैसा दूसरों ने कहा है, उपयोग न करें isPresent()। यह वैकल्पिक के लिए गलत दृष्टिकोण है। उपयोग mapया flatMapइसके बजाय।
एंड्रेस एफ।

1
@CandiedOrange और फिर भी शीर्ष-वोट किए गए उत्तर (उपयोग करने के लिए ifPresent) का परीक्षण करने का एक और तरीका है।
इम्बिसिस

1
@CandiedOrange ifPresent(x)सिर्फ एक परीक्षण जितना है if(present) {x}। वापसी मूल्य अप्रासंगिक है।
इम्बिसिस

1
@ फिर भी, आप वास्तव में परीक्षण करने की जरूरत नहीं है। आप यह नहीं कह सकते हैं "इसलिए हमें परीक्षण नहीं करना है" जब आप वास्तव में, अभी भी परीक्षण कर रहे हैं ... जो आप हैं।
हारून

0

मुझे लगता है कि सबसे बड़ा लाभ यह है कि यदि आपका विधि हस्ताक्षर आपको लौटाता है Employeeतो आप अशक्तता की जांच नहीं करते हैं । आप उस हस्ताक्षर से जानते हैं कि यह कर्मचारी को वापस करने की गारंटी है। आपको विफलता के मामले को संभालने की आवश्यकता नहीं है। एक वैकल्पिक के साथ आप जानते हैं कि आप करते हैं।

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

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


1
सरल तरीके से इस लक्ष्य को हासिल करने के लिए उपयोग कर रहा है @Nullableऔर @ReturnValuesAreNonnullByDefaultएक साथ स्थिर विश्लेषण के साथ।
मआर्टिनस

@maaartinus डेकोरेटर एनोटेशन में स्थैतिक विश्लेषण और प्रलेखन के रूप में अतिरिक्त टूलिंग जोड़ना सरल है, तो एपीआई समय का संकलन करें?
स्लेज

अतिरिक्त टूलिंग जोड़ना वास्तव में सरल है, क्योंकि इसमें कोई कोड परिवर्तन की आवश्यकता नहीं है। इसके अलावा, आप वैसे भी करना चाहते हैं क्योंकि यह अन्य कीड़े भी पकड़ता है। +++ मैंने अपने सभी पैकेजों के package-info.javaसाथ एक तुच्छ स्क्रिप्ट लिखी @ReturnValuesAreNonnullByDefaultहै, इसलिए मुझे केवल इतना करना है @Nullableकि आपको वह स्थान जोड़ना है जहां आपको स्विच करना है Optional। इसका मतलब है कम चर, कोई जोड़ा कचरा और कोई रनटाइम-ओवरहेड। मुझे दस्तावेज़ीकरण देखने की ज़रूरत नहीं है क्योंकि यह दिखाता है कि पद्धति पर मँडराते समय। स्थैतिक विश्लेषण उपकरण गलतियों को पकड़ता है और बाद में अशक्तता बदल जाती है।
Maaartinus

इसके साथ मेरी सबसे बड़ी चिंता Optionalयह है कि यह अशक्तता से निपटने का एक वैकल्पिक तरीका जोड़ता है। यदि यह शुरुआत से ही जावा में था, तो यह ठीक हो सकता है, लेकिन अब यह सिर्फ एक दर्द है। कोटलिन रास्ते पर जाना IMHO से बेहतर होगा। +++ ध्यान दें कि List<@Nullable String>अभी भी तार की एक सूची है, जबकि List<Optional<String>>नहीं है।
मातरिनस

0

मेरा उत्तर है: बस नहीं। कम से कम दो बार सोचें कि क्या यह वास्तव में सुधार है।

एक अभिव्यक्ति (दूसरे जवाब से ली गई) जैसी

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

मूल रूप से अशक्त वापसी के तरीकों से निपटने का सही कार्यात्मक तरीका प्रतीत होता है। यह अच्छा लग रहा है, यह कभी नहीं फेंकता है और यह आपको "अनुपस्थित" मामले को संभालने के बारे में कभी नहीं सोचता है।

यह पुराने स्टाइल के तरीके से बेहतर लगता है

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

जो यह स्पष्ट करता है कि elseक्लॉस हो सकते हैं।

कार्यात्मक शैली

  • पूरी तरह से अलग दिखता है (और इसके लिए अप्रयुक्त लोगों के लिए अपठनीय है)
  • डिबग करने के लिए एक दर्द है
  • "अनुपस्थित" मामले को संभालने के लिए आसानी से नहीं बढ़ाया जा सकता ( orElseयह हमेशा पर्याप्त नहीं होता है)
  • "अनुपस्थित" मामले की अनदेखी करने के लिए गुमराह करता है

किसी भी स्थिति में इसे रामबाण औषधि के रूप में इस्तेमाल नहीं किया जाना चाहिए। यदि यह सार्वभौमिक रूप से लागू था, तो .ऑपरेटर के शब्दार्थ को ?.ऑपरेटर के शब्दार्थ द्वारा प्रतिस्थापित किया जाना चाहिए , एनपीई भूल गया और सभी समस्याओं को अनदेखा कर दिया गया।


एक बड़ा जावा प्रशंसक होने के नाते, मुझे यह कहना चाहिए कि कार्यात्मक शैली सिर्फ एक गरीब जावा आदमी के बयान का विकल्प है

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

अन्य भाषाओं से अच्छी तरह से जाना जाता है। क्या हम इस कथन से सहमत हैं

  • (वास्तव में) कार्यात्मक नहीं है?
  • पुराने शैली के कोड के समान है, बस बहुत सरल है?
  • कार्यात्मक शैली की तुलना में बहुत अधिक पठनीय है?
  • डिबगर-फ्रेंडली है?

आपको लगता है कि इस अवधारणा के कार्यान्वयन के लिए एक कोर लाइब्रेरी क्लास के साथ जावा के कार्यान्वयन के लिए पूरी तरह से अलग भाषा विशेषता के साथ इस अवधारणा के कार्यान्वयन का वर्णन किया गया है । वे मेरे जैसे ही दिखते हैं
कैलथ

@ कैलेथ कोई रास्ता नहीं। हम "एक अशक्त-सुरक्षित मूल्यांकन को सरल बनाने की अवधारणा" के बारे में बोल सकते हैं, लेकिन मैं इसे "अवधारणा" नहीं कहूंगा। हम कह सकते हैं कि जावा एक मुख्य पुस्तकालय वर्ग का उपयोग करके एक ही चीज़ को लागू करता है, लेकिन यह सिर्फ एक गड़बड़ है। बस इसके साथ शुरू करें employeeService.getEmployee().getId().apply(id => System.out.println(id));और सुरक्षित नेविगेशन ऑपरेटर का उपयोग करके और एक बार उपयोग करके इसे शून्य-सुरक्षित बनाएं Optional। निश्चित रूप से, हम इसे "कार्यान्वयन विवरण" कह सकते हैं, लेकिन कार्यान्वयन मायने रखता है। ऐसे मामले हैं जब लामाओं ने कोड को सरल और अच्छा बना दिया है, लेकिन यहां यह केवल एक बदसूरत दुरुपयोग है।
मारार्टिनस 12

पुस्तकालय वर्ग: Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);भाषा सुविधा employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));:। दो वाक्यविन्यास, लेकिन वे मुझे बहुत समान दिखते हैं । और "कॉन्सेप्ट" Optionalउर्फ Nullableउर्फ हैMaybe
कैलथ

मुझे समझ नहीं आता कि आप क्यों सोचते हैं कि उनमें से एक "बदसूरत दुरुपयोग" है, और दूसरा अच्छा है। मैं तुम्हें समझ में सोच सकता है दोनों "बदसूरत दुरुपयोग" या दोनों अच्छा।
कैलथ

@ कैलेथ एक अंतर यह है कि आपको दो प्रश्न चिह्न जोड़ने के बजाय, आपको इसे फिर से लिखना होगा। समस्या यह नहीं है कि भाषा सुविधा और लाइब्रेरी वर्ग कोड बहुत भिन्न हैं। ठीक है। समस्या यह है कि मूल कोड और लाइब्रेरी क्लास कोड बहुत भिन्न होते हैं। समान विचार, अलग कोड। यह बहुत बुरा है। +++अशक्तता को संभालने के लिए जो गालियां दी जाती हैं, वे हैं लामा। लमदास ठीक हैं, लेकिन Optionalनहीं है। अशक्तता को कोटलिन में एक उचित भाषा समर्थन की आवश्यकता होती है। एक और समस्या: इससे List<Optional<String>>संबंधित नहीं है List<String>, जहाँ हमें उनसे संबंधित होने की आवश्यकता होगी।
Maaartinus

0

वैकल्पिक एक अवधारणा (एक उच्च क्रम प्रकार) कार्यात्मक प्रोग्रामिंग से आ रहा है। इसका उपयोग नेस्टेड नल जाँच से दूर करता है और आपको कयामत के पिरामिड से बचाता है।

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