वैकल्पिक या जावा में वैकल्पिक वैकल्पिक


137

मैं जावा 8 में नए वैकल्पिक प्रकार के साथ काम कर रहा हूं, और मैं एक सामान्य ऑपरेशन की तरह लगता है जो कार्यात्मक रूप से समर्थित नहीं है: "orElseOptional"

निम्नलिखित पैटर्न पर विचार करें:

Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
    Optional<Result> resultFromServiceB = serviceB(args);
    if (resultFromServiceB.isPresent) return resultFromServiceB;
    else return serviceC(args);
}

इस पैटर्न के कई रूप हैं, लेकिन यह एक वैकल्पिक विकल्प पर "orElse" चाहने के लिए उकसाता है जो एक नया वैकल्पिक उत्पादन करने वाले फ़ंक्शन को लेता है, जिसे केवल वर्तमान एक मौजूद नहीं है।

यह कार्यान्वयन इस तरह दिखेगा:

public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
    return value != null ? this : other.get();
}

अगर इस तरह की कोई विधि मौजूद नहीं है, तो मैं उत्सुक हूं, अगर मैं सिर्फ अनपेक्षित तरीके से वैकल्पिक का उपयोग कर रहा हूं, और इस मामले से निपटने के लिए लोग और क्या तरीके अपनाते हैं।

मुझे यह कहना चाहिए कि मुझे लगता है कि कस्टम यूटिलिटी क्लासेस / मेथड से जुड़े समाधान सुरुचिपूर्ण नहीं हैं क्योंकि मेरे कोड के साथ काम करने वाले लोग जरूरी नहीं जानते कि वे मौजूद हैं।

इसके अलावा, अगर किसी को पता है, तो क्या इस तरह की विधि को जेडीके 9 में शामिल किया जाएगा, और मैं ऐसी विधि का प्रस्ताव कहां रख सकता हूं? यह मुझे करने के लिए एपीआई के लिए एक बहुत चमक चूक की तरह लगता है।


13
इस मुद्दे को देखें । स्पष्ट करने के लिए: यह पहले से ही जावा 9 में होने जा रहा है - नहीं तो जावा 8. के भविष्य के अद्यतन में
Obicere

यह है! धन्यवाद, मेरी खोज में ऐसा नहीं मिला।
योना एपेलेट्री

2
@Obicere यह मुद्दा यहां लागू नहीं होता है क्योंकि यह खाली वैकल्पिक पर व्यवहार के बारे में है , वैकल्पिक परिणाम के बारे में नहीं । वैकल्पिक के पास पहले से ही orElseGet()ओपी की जरूरत है, केवल यह अच्छा कैस्केडिंग सिंटैक्स उत्पन्न नहीं करता है।
मार्को टोपोलनिक


1
जावा वैकल्पिक पर महान ट्यूटोरियल: codeflex.co/java-optional-no-more-nullpointerexception
जॉन डेट्रायट

जवाबों:


86

यह JDK 9 के भाग के रूप में है or, जो एक लेता है Supplier<Optional<T>>। आपका उदाहरण तब होगा:

return serviceA(args)
    .or(() -> serviceB(args))
    .or(() -> serviceC(args));

विवरण के लिए जावदोक या यह पोस्ट मैंने लिखी है।


अच्छा लगा। इसके अलावा एक साल पुराना होना चाहिए और मैंने ध्यान नहीं दिया। आपके ब्लॉग में प्रश्न के बारे में, रिटर्न प्रकार बदलने से बाइनरी संगतता टूट जाएगी, क्योंकि बायटेकोड मंगलाचरण निर्देश पूर्ण हस्ताक्षर को संदर्भित करते हैं, जिसमें रिटर्न प्रकार भी शामिल है, इसलिए रिटर्न प्रकार को बदलने का कोई मौका नहीं है ifPresent। लेकिन वैसे भी, मुझे लगता है कि नाम ifPresentवैसे भी अच्छा नहीं है। नाम में अन्य सभी तरीकों नहीं असर "और" (जैसे map, filter, flatMap), यह अर्थ निकलता है कि वे कुछ भी नहीं है, तो कोई मूल्य नहीं मौजूद है, तो क्यों चाहिए ifPresent...
होल्गर

इसलिए Optional<T> perform(Consumer<T> c)चाइनिंग की अनुमति देने के लिए एक विधि को जोड़ना perform(x).orElseDo(y)( orElseDoआपके प्रस्तावित के विकल्प के रूप में ifEmpty, elseअनुपस्थित मूल्यों के लिए कुछ करने के लिए सभी विधि के नाम पर संगत होना चाहिए )। आप performजावा 9 की नकल कर सकते हैं, stream().peek(x).findFirst()हालांकि इसके माध्यम से एपीआई का दुरुपयोग है और एक ही समय में Runnableनिर्दिष्ट किए बिना निष्पादित करने का कोई तरीका नहीं है Consumer...
Holger

65

वर्तमान API दिया गया सबसे साफ "प्रयास सेवाएं" दृष्टिकोण होगा:

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

महत्वपूर्ण पहलू परिचालन की एक (स्थिर) श्रृंखला नहीं है जिसे आपको एक बार लिखना है लेकिन किसी अन्य सेवा को जोड़ना कितना आसान है (या सेवाओं की सूची को संशोधित करना सामान्य है)। यहां, किसी एकल को जोड़ना या निकालना ()->serviceX(args)पर्याप्त है।

धाराओं के आलसी मूल्यांकन के कारण, यदि कोई पूर्ववर्ती सेवा एक गैर-रिक्त लौटाती है, तो किसी भी सेवा को आमंत्रित नहीं किया जाएगा Optional


13
बस एक परियोजना में इस्तेमाल किया, भगवान का शुक्र है कि हम कोड समीक्षा नहीं करते हैं।
इल्या स्मगिन

3
यह "orElseGet" की श्रृंखला की तुलना में बहुत अधिक क्लीनर है, लेकिन इसे पढ़ना भी बहुत कठिन है।
slartidan

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

3
मुझे आश्चर्य है अगर स्विचिंग .map(Optional::get)के साथ .findFirst(), "पढ़ें" करने के लिए यह आसान कर देगा जैसे .filter(Optional::isPresent).findFirst().map(Optional::get)जा सकता है की तरह "धारा जिसके लिए वैकल्पिक :: isPresent सच है में पहला तत्व खोजने के लिए और उसके बाद लागू करने के वैकल्पिक :: पाने से यह समतल" "पढ़ें"?
स्कैप्टन

3
मजेदार, मैंने कुछ महीने पहले इसी तरह के प्रश्न के लिए एक समान समाधान पोस्ट किया था। यह पहली बार है जब मैं इस पार आ रहा हूं।
shmosel

34

यह सुंदर नहीं है, लेकिन यह काम करेगा:

return serviceA(args)
  .map(Optional::of).orElseGet(() -> serviceB(args))
  .map(Optional::of).orElseGet(() -> serviceC(args))
  .map(Optional::of).orElseGet(() -> serviceD(args));

.map(func).orElseGet(sup)के साथ उपयोग के लिए एक काफी आसान पैटर्न है Optional। इसका मतलब है “यदि Optionalइसमें मूल्य शामिल हैं v, तो मुझे देंfunc(v) , अन्यथा मुझे दें sup.get()"।

इस मामले में, हम कॉल करते हैं serviceA(args)और प्राप्त करते हैं Optional<Result>। यदि Optionalइसमें मूल्य है v, तो हम प्राप्त करना चाहते हैं Optional.of(v), लेकिन यदि यह खाली है, तो हम प्राप्त करना चाहते हैं serviceB(args)। अधिक विकल्पों के साथ कुल्ला-दोहराएँ।

इस पैटर्न के अन्य उपयोग हैं

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)

1
हुह, जब मैं इस रणनीति का उपयोग करता हूं, तो ग्रहण कहता है: "विधि orElseGet (आपूर्तिकर्ता <; एक्सटेंडिंग स्ट्रिंग>) प्रकार में वैकल्पिक <स्ट्रिंग> तर्क के लिए लागू नहीं है () (> -> {}" यह प्रतीत नहीं होता है स्ट्रिंग पर एक वैकल्पिक वैध रणनीति को वापस करने पर विचार करने के लिए ??
क्रिस्मार्क्स

@chrismarx () -> {}वापस नहीं लौटा Optional। आप क्या खत्म करने की कोशिश कर रहे हैं?
मिशा

1
मैं सिर्फ उदाहरण का पालन करने की कोशिश कर रहा हूं। मैप कॉल को चेंज करना काम नहीं कर रहा है। मेरी सेवाएं वापस लौटती हैं, और निश्चित रूप से वहाँ कोई .map () विकल्प उपलब्ध नहीं है
chrismarx

1
or(Supplier<Optional<T>>)जावा 9 के आगामी के लिए उत्कृष्ट पठनीय विकल्प
डेविड एम।

1
@ शीशी तुम गलत हो। .map()एक खाली पर एक खाली Optionalउत्पादन होगा Optional
मीशा

27

शायद यह वही है जो आप के बाद कर रहे हैं: एक वैकल्पिक या किसी अन्य से मूल्य प्राप्त करें

अन्यथा, आप एक नज़र रखना चाह सकते हैं Optional.orElseGet। यहाँ एक उदाहरण है कि मुझे क्या लगता है कि आप इसके बाद हैं:

result = Optional.ofNullable(serviceA().orElseGet(
                                 () -> serviceB().orElseGet(
                                     () -> serviceC().orElse(null))));

2
यह आमतौर पर जीनियस करते हैं। वैकल्पिक को एक अशक्त के रूप में मूल्यांकन करें और इसे लपेटकर ofNullableसबसे अच्छी चीज है जिसे मैंने कभी देखा है।
जिन क्वॉन

5

यह मानते हुए कि आप अभी भी JDK8 पर हैं, कई विकल्प हैं।

विकल्प # 1: अपनी स्वयं की सहायक विधि बनाएं

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

public class Optionals {
    static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
        return Arrays.stream(optionals)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst()
                .orElseGet(Optional::empty);
    }
}

ताकि आप कर सके:

return Optionals.or(
   ()-> serviceA(args),
   ()-> serviceB(args),
   ()-> serviceC(args),
   ()-> serviceD(args)
);

विकल्प # 2: एक पुस्तकालय का उपयोग करें

उदाहरण के लिए Google अमरूद का वैकल्पिक एक उचित or()संचालन (JDK9 की तरह) का समर्थन करता है , जैसे:

return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));

(जहां सेवाओं में से प्रत्येक वापस आती है com.google.common.base.Optional, बजाय java.util.Optional)।


मुझे Optional<T>.or(Supplier<Optional<T>>)अमरूद डॉक्स में नहीं मिला । क्या आपके पास उसके लिए लिंक है?
तमसा हेगड़ेस

.orElseGet T लौटाता है लेकिन वैकल्पिक :: खाली रिटर्न वैकल्पिक <T>। Optional.ofNullable (........ orElse (null)) पर @aioobe द्वारा वर्णित वांछित प्रभाव है।
मिगुएल परेरा

@TamasHegedus आप विकल्प # 1 में हैं? यह एक कस्टम कार्यान्वयन है जो इसके ऊपर के स्निप पर है। पीएस: देर से जवाब के लिए खेद है
भेड़

इस मामले में @MiguelPereira .orElseGet वैकल्पिक <T> लौटाता है क्योंकि यह वैकल्पिक <Optional <T >> पर चल रहा है
भेड़

2

यह पैटर्न मिलान के लिए एक अच्छा फिट और कुछ और कोई कार्यान्वयन के साथ एक अधिक पारंपरिक विकल्प इंटरफ़ेस (जैसे कि Javaslang , FunctionalJava में ) या एक आलसी हो सकता है जैसा कि शायद साइक्लॉप्स-रिएक्शन में लागू होता है। मैं इस पुस्तकालय के लेखक हूं

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

  import static com.aol.cyclops.Matchables.optional;

  optional(serviceA(args)).visit(some -> some , 
                                 () -> optional(serviceB(args)).visit(some -> some,
                                                                      () -> serviceC(args)));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.