जावा 8: लैम्ब्डा-स्ट्रीम्स, एक्सेप्शन के साथ विधि द्वारा फ़िल्टर


168

मुझे जावा 8 के लैम्ब्डा एक्सप्रेशन को आज़माने में समस्या है। आमतौर पर यह ठीक काम करता है, लेकिन अब मेरे पास ऐसे तरीके हैं जो फेंक देते हैं IOException। यदि आप निम्नलिखित कोड को देखते हैं तो यह सबसे अच्छा है:

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

समस्या यह है, यह संकलित नहीं करता है, क्योंकि मुझे isActive- और getNumber-Methods के संभावित अपवादों को पकड़ना है। लेकिन भले ही मैं स्पष्ट रूप से नीचे की तरह एक कोशिश-कैच-ब्लॉक का उपयोग करता हूं, यह अभी भी संकलित नहीं करता है क्योंकि मैं अपवाद को नहीं पकड़ता हूं। तो या तो JDK में एक बग है, या मुझे नहीं पता कि इन अपवादों को कैसे पकड़ा जाए।

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

मैं इसे कैसे काम कर सकता हूं? क्या कोई मुझे सही समाधान के लिए संकेत दे सकता है?




4
सरल और सही उत्तर: लैम्ब्डा के अंदर अपवाद को पकड़ें।
ब्रायन गोएट्ज़

जवाबों:


211

लैम्ब्डा के भागने से पहले आपको अपवाद को पकड़ना चाहिए :

s = s.filter(a -> { try { return a.isActive(); } 
                    catch (IOException e) { throw new UncheckedIOException(e); }}});

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

आप अपने लंबोदर के एक रैपर का उपयोग करके इससे निपट सकते हैं जो अनियंत्रित लोगों के लिए चेक किए गए अपवादों का अनुवाद करता है:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (RuntimeException e) { throw e; }
  catch (Exception e) { throw new RuntimeException(e); }
}

आपके उदाहरण के रूप में लिखा जाएगा

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

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

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
  try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }


@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

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


4
मैंने देखा है कि पहले NettyIO "डरपोक फेंक" करते हैं, और मैं अपनी कुर्सी खिड़की से बाहर फेंकना चाहता था। "क्या? कहाँ से जाँच की अपवाद रिसाव?" डरपोक फेंक के लिए यह पहला कानूनी उपयोग मामला है जो मैंने अभी तक देखा है। एक प्रोग्रामर के रूप में आपको चुपके से फेंकने के संकेत के बारे में सतर्क रहना होगा। शायद बेहतर है कि एक और स्ट्रीम इंटरफ़ेस / इंप्रेशन बनाएं जो चेक किए गए अपवाद का समर्थन करता है?
केविनरपे

8
कोई भी समझदार एपीआई ग्राहक को अघोषित चेक अपवाद नहीं देना चाहिए, यह सुनिश्चित है। एपीआई के भीतर एक समझ हो सकती है, हालांकि, जाँच की गई अपवाद लीक हो सकता है। जब तक वे सामान्य विफलता का सिर्फ एक और संकेत नहीं हैं, तब तक नुकसान नहीं पहुंचाते हैं और पकड़े जाने और थोक को संभालने के बारे में हैं।
मार्को टोपोलनिक

5
@kevinarpe यह सटीक कारण है कि डरपोक फेंकता एक बुरा विचार है। संकलक को शॉर्टक्रीक्यूटिंग करने से भविष्य के रखरखाव को भ्रमित करने के लिए बाध्य है।
Thorbjørn रावन एंडरसन

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

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

29

आप अपने स्थिर दर्द को लैम्ब्डा के साथ भी प्रचारित कर सकते हैं, इसलिए पूरी बात पढ़ने योग्य लगती है:

s.filter(a -> propagate(a::isActive))

propagateयहां java.util.concurrent.Callableएक पैरामीटर के रूप में प्राप्त होता है और कॉल के दौरान पकड़े गए किसी भी अपवाद को धर्मान्तरित करता है RuntimeException। एक समान रूपांतरण विधि है थ्रावेबल्स # प्रपोजेट (थ्रोएबल) अमरूद में।

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

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}

22

यह UtilExceptionसहायक कक्षा आपको जावा स्ट्रीम में किसी भी चेक किए गए अपवाद का उपयोग करने देती है, जैसे:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

नोट Class::forNameफेंकता है ClassNotFoundException, जिसे चेक किया जाता है । धारा स्वयं भी फेंकता है ClassNotFoundException, और कुछ अनियंत्रित अपवाद को लपेटता नहीं है।

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
    R apply(T t) throws E;
    }

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
    return t -> {
        try { return function.apply(t); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }

}

इसका उपयोग करने के तरीके (सांख्यिकीय आयात के बाद UtilException) पर कई अन्य उदाहरण :

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

लेकिन निम्नलिखित फायदे, नुकसान और सीमाओं को समझने से पहले इसका उपयोग न करें :

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

• यदि कॉलिंग-कोड पहले से ही चेक किए गए अपवाद को संभालता है, तो कंपाइलर आपको थ्रो क्लॉज को मेथड डिक्लेरेशन में जोड़ने के लिए याद दिलाता है जिसमें स्ट्रीम शामिल है (यदि आप ऐसा नहीं कहेंगे: अपवाद को कभी भी दिए गए स्टेटमेंट के बॉडी में नहीं फेंका जाता है )।

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

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

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

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

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

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

2) यह कम से कम आश्चर्य के सिद्धांत का उल्लंघन करता है: सभी संभावित अपवादों को पकड़ने की गारंटी देने में सक्षम होने के लिए यह रनटाइम अपवाद को पकड़ने के लिए पर्याप्त नहीं होगा। इस कारण से, मेरा मानना ​​है कि इसे फ्रेमवर्क कोड में नहीं किया जाना चाहिए, बल्कि केवल व्यावसायिक कोड में जिसे आप पूरी तरह से नियंत्रित करते हैं।

निष्कर्ष में: मेरा मानना ​​है कि यहां की सीमाएं गंभीर नहीं हैं, और UtilExceptionवर्ग का उपयोग बिना किसी भय के किया जा सकता है। हालाँकि, यह आप पर निर्भर है!


8

आप Streamअनियंत्रित अपवाद को फेंकने के लिए अपने लैंबडा को लपेटकर संभावित रूप से अपने स्वयं के संस्करण को रोल कर सकते हैं और फिर बाद में टर्मिनल संचालन पर अनियंत्रित अपवाद को हटा सकते हैं:

@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
    public boolean test(T t) throws X;
}

@FunctionalInterface
public interface ThrowingFunction<T, R, X extends Throwable> {
    public R apply(T t) throws X;
}

@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}

public interface ThrowingStream<T, X extends Throwable> {
    public ThrowingStream<T, X> filter(
            ThrowingPredicate<? super T, ? extends X> predicate);

    public <R> ThrowingStream<T, R> map(
            ThrowingFunction<? super T, ? extends R, ? extends X> mapper);

    public <A, R> R collect(Collector<? super T, A, R> collector) throws X;

    // etc
}

class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamAdapter(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new AdapterException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamAdapter<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamAdapter<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (AdapterException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskException(() -> delegate.collect(collector));
    }
}

तब आप इसे उसी तरह से उपयोग कर सकते हैं Stream:

Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());

इस समाधान के लिए बॉयलरप्लेट की काफी आवश्यकता होगी, इसलिए मेरा सुझाव है कि आप पहले से ही बनाई गई लाइब्रेरी पर एक नज़र डालें, जो कि मैंने पूरी Streamकक्षा के लिए यहाँ वर्णन किया है (और अधिक!)।


हैलो ... छोटे बग? new StreamBridge<>(ts, IOException.class);->new StreamBridge<>(s, IOException.class);
केविनरपे

1
@kevinarpe हां। यह भी कहना चाहिए था StreamAdapter
जेफरी

5

# प्रॉपगेट () विधि का उपयोग करें। सैम बेरन द्वारा जावा 8 ब्लॉग से गैर-अमरूद कार्यान्वयन का नमूना :

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}

Java 8 ब्लॉग लिंक मृत है।
स्पिकियो

4

यह सीधे सवाल का जवाब नहीं देता है (कई अन्य जवाब हैं जो करते हैं) लेकिन पहली जगह में समस्या से बचने की कोशिश करता है:

मेरे अनुभव में Stream(या अन्य लैंबडा अभिव्यक्ति) में अपवादों को संभालने की आवश्यकता अक्सर इस तथ्य से आती है कि अपवादों को उन तरीकों से फेंकने की घोषणा की जाती है जहां उन्हें फेंकना नहीं चाहिए। यह अक्सर व्यापार तर्क को इन और आउटपुट के साथ मिलाकर आता है। आपका Accountइंटरफ़ेस एक आदर्श उदाहरण है:

interface Account {
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
}

IOExceptionप्रत्येक गाइटर पर फेंकने के बजाय , इस डिज़ाइन पर विचार करें:

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

विधि AccountReader.readAccount(…)एक डेटाबेस या एक फ़ाइल या जो कुछ भी खाता है और यदि वह सफल नहीं होता है तो एक अपवाद को पढ़ सकता है। यह एक ऐसी Accountवस्तु का निर्माण करता है जिसमें पहले से ही सभी मूल्य शामिल हैं, जिसका उपयोग करने के लिए तैयार है। चूंकि मान पहले से लोड किए गए हैं readAccount(…), इसलिए गेटर्स एक अपवाद नहीं फेंकेंगे। इस प्रकार आप अपवादों को लपेटने, मास्किंग या छिपाने की आवश्यकता के बिना उन्हें लंबोदर में स्वतंत्र रूप से उपयोग कर सकते हैं।

बेशक, यह हमेशा संभव नहीं है कि जिस तरह से मैंने वर्णन किया है, लेकिन अक्सर यह होता है और यह पूरी तरह से क्लीनर कोड की ओर जाता है (IMHO):

  • चिंताओं का बेहतर पृथक्करण और एकल जिम्मेदारी सिद्धांत का पालन करना
  • कम बॉयलरप्लेट: आपको अपने कोड के साथ अव्यवस्था नहीं करनी है throws IOException लिए उपयोग करने के लिए नहीं बल्कि कंपाइलर को संतुष्ट करने की आवश्यकता नहीं है
  • त्रुटि से निपटने: आप उन त्रुटियों को संभालते हैं जहां वे होते हैं - जब किसी फ़ाइल या डेटाबेस से पढ़ते हैं - तो कहीं न कहीं आपके व्यापार तर्क के बीच में केवल इसलिए कि आप एक फ़ील्ड मान प्राप्त करना चाहते हैं
  • आप अपरिवर्तनीय बनाने में सक्षम हो सकते हैंAccount इसके लाभों से और लाभ कमा सकते हैं (जैसे धागा सुरक्षा)
  • आपको Accountलैम्ब्डा (उदाहरण के लिए Stream) में उपयोग करने के लिए "गंदे चाल" या वर्कआर्ड की आवश्यकता नहीं है

4

इसके साथ सरल कोड के नीचे से हल किया जा सकता स्ट्रीम और कोशिश में AbacusUtil :

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

प्रकटीकरण : मैं का डेवलपर हूँ AbacusUtil


3

@Marcg सॉल्यूशन का विस्तार करते हुए, आप सामान्य रूप से स्ट्रीम में चेक किए गए अपवाद को फेंक और पकड़ सकते हैं ; यह है, संकलक आपको पकड़ने / फिर से फेंकने के लिए कहेगा जैसा कि आप बाहर की धाराएँ थे !!

@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
    boolean test(T t) throws E;
}

/**
 * .filter(rethrowPredicate(t -> t.isActive()))
 */
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
    return t -> {
        try {
            return predicate.test(t);
        } catch (Exception exception) {
            return throwActualException(exception);
        }
    };
}

@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwActualException(Exception exception) throws E {
    throw (E) exception;
}

फिर, आपका उदाहरण निम्नानुसार लिखा जाएगा (इसे अधिक स्पष्ट रूप से दिखाने के लिए परीक्षण जोड़ना):

@Test
public void testPredicate() throws MyTestException {
    List<String> nonEmptyStrings = Stream.of("ciao", "")
            .filter(rethrowPredicate(s -> notEmpty(s)))
            .collect(toList());
    assertEquals(1, nonEmptyStrings.size());
    assertEquals("ciao", nonEmptyStrings.get(0));
}

private class MyTestException extends Exception { }

private boolean notEmpty(String value) throws MyTestException {
    if(value==null) {
        throw new MyTestException();
    }
    return !value.isEmpty();
}

@Test
public void testPredicateRaisingException() throws MyTestException {
    try {
        Stream.of("ciao", null)
                .filter(rethrowPredicate(s -> notEmpty(s)))
                .collect(toList());
        fail();
    } catch (MyTestException e) {
        //OK
    }
}

यह उदाहरण संकलित नहीं करता है
रोमन एम

हाय @ROMM यह इंगित करने के लिए धन्यवाद: मैंने विधि "थ्रोएक्चुअल एक्ससेप्शन" पर लापता रिटर्न प्रकार को ठीक किया है। हम उत्पादन में इसका उपयोग कर रहे हैं इसलिए मुझे आशा है कि यह आपके पक्ष में भी काम कर रहा है
पाओलोक

3

IOException (RuntimeException) हैंडलिंग कोड को ठीक से जोड़ने के लिए, आपका तरीका इस तरह दिखेगा:

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

अब समस्या यह है कि IOExceptionवसीयत को एक के रूप में कब्जा कर RuntimeExceptionवापस ए में परिवर्तित कर दिया जाएगाIOException - और यह उपरोक्त विधि में और भी अधिक कोड जोड़ देगा।

क्यों का उपयोग करें Streamजब यह इस तरह से किया जा सकता है तो इसका - और यह विधि फेंकता है IOExceptionतो उसके लिए कोई अतिरिक्त कोड की भी आवश्यकता नहीं है:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;

1

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

stream().map(unchecked(URI::new)) //with a static import

https://github.com/TouK/ThrowingFunction/


1

आपके उदाहरण के रूप में लिखा जा सकता है:

import utils.stream.Unthrow;

class Bank{
   ....
   public Set<String> getActiveAccountNumbers() {
       return accounts.values().stream()
           .filter(a -> Unthrow.wrap(() -> a.isActive()))
           .map(a -> Unthrow.wrap(() -> a.getNumber()))
           .collect(Collectors.toSet());
   }
   ....
}

यहाँ पर Unthrow क्लास ली जा सकती है https://github.com/SeregaLBN/StreamUnthrower


0

यदि आप तृतीय पक्ष पुस्तकालयों का उपयोग करने में कोई आपत्ति नहीं करते हैं, तो एओएल के साइक्लोप्स-रिएक्शन लिब, डिस्क्लोजर :: मैं एक योगदानकर्ता हूं, एक एक्ससेप्शन सॉफ्टनर वर्ग है जो यहां मदद कर सकता है।

 s.filter(softenPredicate(a->a.isActive()));

0

जावा में कार्यात्मक इंटरफेस किसी भी जाँच या अनियंत्रित अपवाद की घोषणा नहीं करते हैं। हमें निम्नलिखित तरीकों से अपने हस्ताक्षर बदलने होंगे:

boolean isActive() throws IOException; 
String getNumber() throwsIOException;

सेवा:

boolean isActive();
String getNumber();

या इसे ट्राई-कैच ब्लॉक से हैंडल करें:

public Set<String> getActiveAccountNumbers() {
  Stream<Account> s =  accounts.values().stream();
  s = s.filter(a -> 
    try{
      a.isActive();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  Stream<String> ss = s.map(a -> 
    try{
      a.getNumber();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  return ss.collect(Collectors.toSet());
}

एक अन्य विकल्प एक कस्टम आवरण लिखना है या थ्रोइंगफंक्शन जैसी लाइब्रेरी का उपयोग करना है। पुस्तकालय के साथ हमें केवल अपने pom.xml पर निर्भरता जोड़ने की आवश्यकता है:

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

और ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier जैसे विशिष्ट वर्गों का उपयोग करें।

अंत में कोड इस तरह दिखता है:

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}

0

मुझे स्ट्रीम (जावा -8) में चेक किए गए अपवाद को संभालने का कोई तरीका दिखाई नहीं देता, मैंने जो एकमात्र तरीका लागू किया है, वह स्ट्रीम में अपवाद की जाँच कर रहा है और उन्हें अनियंत्रित अपवाद के रूप में फिर से फेंक रहा है।

        Arrays.stream(VERSIONS)
        .map(version -> TemplateStore.class
                .getClassLoader().getResourceAsStream(String.format(TEMPLATE_FILE_MASK, version)))
        .map(inputStream -> {
            try {
                return ((EdiTemplates) JAXBContext.newInstance(EdiTemplates.class).createUnmarshaller()
                        .unmarshal(inputStream)).getMessageTemplate();
            } catch (JAXBException e) {
                throw new IllegalArgumentException(ERROR, e);
            }})
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

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

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

0

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

@Test
public void whenValuePrinted_thenPrintValue() {

    List<Integer> intStream = Arrays.asList(0, 1, 2, 3, 4, 5, 6);
    intStream.stream().map(Either.liftWithValue(item -> doSomething(item)))
             .map(item -> item.isLeft() ? item.getLeft() : item.getRight())
             .flatMap(o -> {
                 System.out.println(o);
                 return o.isPresent() ? Stream.of(o.get()) : Stream.empty();
             })
             .forEach(System.out::println);
}

private Object doSomething(Integer item) throws Exception {

    if (item == 0) {
        throw new Exception("Zero ain't a number!");
    } else if (item == 4) {
        return Optional.empty();
    }

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