मैं Java 8 स्ट्रीम के अंदर से CHECKED अपवाद कैसे फेंक सकता हूं?


287

मैं Java 8 स्ट्रीम / लैम्ब्डा के अंदर से CHECKED अपवाद कैसे फेंक सकता हूं?

दूसरे शब्दों में, मैं इस संकलन की तरह कोड बनाना चाहता हूं:

public List<Class> getClasses() throws ClassNotFoundException {     

    List<Class> classes = 
        Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
              .map(className -> Class.forName(className))
              .collect(Collectors.toList());                  
    return classes;
    }

यह कोड संकलित नहीं करता है, चूंकि Class.forName()थ्रो से ऊपर की विधि ClassNotFoundException, जिसे चेक किया गया है।

कृपया ध्यान दें कि मैं एक रनटाइम अपवाद के अंदर चेक किए गए अपवाद को लपेटना नहीं चाहता हूं और इसके बजाय लिपटे अनियंत्रित अपवाद को फेंकना चाहता हूं। मैं जाँच किए गए अपवाद को स्वयं फेंकना चाहता हूं , और बिना किसी कुरूप try/ catchesधारा को जोड़े ।


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

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

22
@ ब्रायन, सच कहूँ तो, जब लोग बयानों के लिए विरासत को फिर से भरने की कोशिश करते हैं, तो उनमें से आधी धाराएँ परिवर्तित हो जाती हैं, लेकिन दूसरे आधे हिस्से में वे रिफलेक्टरिंग छोड़ देते हैं, क्योंकि कोई भी इन कोशिशों / कैच को जोड़ना नहीं चाहता है। वे कोड को पढ़ने के लिए बहुत कठिन प्रस्तुत करते हैं, निश्चित रूप से मूल फॉर-स्टेटमेंट की तुलना में अधिक। ऊपर दिए गए मेरे कोड उदाहरण में, जब तक आप "फेंकता ClassNotFoundException" को बनाए रखते हैं, मुझे बाहरी कोड में कोई अंतर नहीं दिखता है। क्या आप मुझे कुछ वास्तविक जीवन के उदाहरण दे सकते हैं जहां यह अपवादों के बारे में नियम तोड़ता है?
मार्कजी

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

14
यह कोड अव्यवस्था आपत्ति को संबोधित नहीं करता है, क्योंकि तब आपको मूल अपवाद को अनचेक करने और पुनर्व्यवस्थित करने के लिए स्ट्रीम के चारों ओर दूसरे प्रयास / कैच की आवश्यकता होती है। इसके विपरीत, यदि आप चेक किए गए अपवाद को फेंक देते हैं, तो आपको केवल throws ClassNotFoundExceptionउस विधि घोषणा में रखने की आवश्यकता है जिसमें स्ट्रीम शामिल है, ताकि कॉलिंग कोड को उम्मीद हो और चेक किए गए अपवाद को पकड़ने की अनुमति मिल सके।
मार्क जी

जवाबों:


250

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

मेरी राय में, यह एपीआई में एक विशाल बग और भाषा विनिर्देश में एक मामूली बग है ।

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

भाषा विनिर्देश में बग यह है कि यह एक प्रकार के पैरामीटर को एक प्रकार के बजाय एक प्रकार की सूची को अनुमान लगाने की अनुमति नहीं देता है जब तक कि प्रकार पैरामीटर केवल उन स्थितियों में उपयोग किया जाता है जहां प्रकारों की एक सूची अनुमेय है ( throwsखंड)।

जावा प्रोग्रामर के रूप में हमारी अपेक्षा यह है कि निम्नलिखित कोड को संकलित किया जाना चाहिए:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class CheckedStream {
    // List variant to demonstrate what we actually had before refactoring.
    public List<Class> getClasses(final List<String> names) throws ClassNotFoundException {
        final List<Class> classes = new ArrayList<>();
        for (final String name : names)
            classes.add(Class.forName(name));
        return classes;
    }

    // The Stream function which we want to compile.
    public Stream<Class> getClasses(final Stream<String> names) throws ClassNotFoundException {
        return names.map(Class::forName);
    }
}

हालाँकि, यह देता है:

cher@armor1:~/playground/Java/checkedStream$ javac CheckedStream.java 
CheckedStream.java:13: error: incompatible thrown types ClassNotFoundException in method reference
        return names.map(Class::forName);
                         ^
1 error

जिस तरह से कार्यात्मक इंटरफेस को परिभाषित किया गया है, वर्तमान में कंपाइलर को अपवाद को आगे बढ़ाने से रोकता है - कोई घोषणा नहीं है जो बताएगी Stream.map()कि यदि Function.apply() throws E, Stream.map() throws Eसाथ ही।

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

import java.io.IOException;
interface Function<T, R, E extends Throwable> {
    // Declare you throw E, whatever that is.
    R apply(T t) throws E;
}   

interface Stream<T> {
    // Pass through E, whatever mapper defined for E.
    <R, E extends Throwable> Stream<R> map(Function<? super T, ? extends R, E> mapper) throws E;
}   

class Main {
    public static void main(final String... args) throws ClassNotFoundException {
        final Stream<String> s = null;

        // Works: E is ClassNotFoundException.
        s.map(Class::forName);

        // Works: E is RuntimeException (probably).
        s.map(Main::convertClass);

        // Works: E is ClassNotFoundException.
        s.map(Main::throwSome);

        // Doesn't work: E is Exception.
        s.map(Main::throwSomeMore);  // error: unreported exception Exception; must be caught or declared to be thrown
    }   

    public static Class convertClass(final String s) {
        return Main.class;
    }   

    static class FooException extends ClassNotFoundException {}

    static class BarException extends ClassNotFoundException {}

    public static Class throwSome(final String s) throws FooException, BarException {
        throw new FooException();
    }   

    public static Class throwSomeMore(final String s) throws ClassNotFoundException, IOException  {
        throw new FooException();
    }   
}   

मामले में throwSomeMoreहम IOExceptionचूकते हुए देखना चाहेंगे , लेकिन यह वास्तव में याद आती है Exception

यह बिल्कुल सही नहीं है क्योंकि अपवाद एक एकल प्रकार की तलाश में लगता है, यहां तक ​​कि अपवादों के मामले में भी। क्योंकि प्रकार के अनुमान के लिए एक ही प्रकार की Eआवश्यकता होती है , एक सामान्य हल करने की आवश्यकता होती superहै ClassNotFoundExceptionऔर IOException, जो है Exception

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

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

इससे भी बुरी खबर यह है कि इस विषय पर पहले ही 2010 में ब्रायन गोएत्ज़ ने चर्चा की थी https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_java (नया लिंक: http://mail.openjjk.java.net/pipermail/lambda -देव / 2010-जून / 001484.html ) लेकिन मुझे सूचित किया गया है कि यह जांच अंतत: समाप्त नहीं हुई है, और यह कि ओरेकल में कोई वर्तमान काम नहीं है कि मैं जाँच किए गए अपवादों और लंबोदा के बीच की बातचीत को कम करना जानता हूं।


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

5
मैं अनुमान लगाता हूं कि कुछ वर्षों में, धाराओं के अंदर जाँच किए गए अपवादों से निपटने की कठिनाई इन दो परिणामों में से एक को जन्म देगी: लोग बस जाँच किए गए अपवादों का उपयोग करना बंद कर देंगे, या हर कोई कुछ हैक का उपयोग करना शुरू कर देगा जैसे कि मैंने जो पोस्ट किया है मेरा UtilException जवाब। मैं शर्त लगा सकता हूँ कि जावा -8 धाराएँ जाँच किए गए अपवादों के ताबूत पर अंतिम कील हैं, इस तथ्य के लिए नहीं थीं कि जाँच किए गए अपवाद जेडीके का हिस्सा हैं। हालाँकि मुझे व्यवसाय कोड में (कुछ विशिष्ट उपयोग के मामलों के लिए) जाँच अपवाद पसंद है और मैं इसका उपयोग करता हूँ, मैंने सभी सामान्य JDK अपवादों को विस्तारित किया है।
मार्कज

9
@ यूनीहेड्रो समस्या यह है कि कार्यात्मक इंटरफेस अपवादों को आगे नहीं बढ़ाते हैं। मुझे लैम्ब्डा के अंदरtry-catch ब्लॉक की आवश्यकता होगी , और बस इसका कोई मतलब नहीं है। जैसे ही मेमने में किसी तरह से उपयोग किया जाता है, उदाहरण के लिए , समस्या है। मूल रूप से, चेक किए गए अपवादों को फेंकने वाले तरीकों को कार्यात्मक प्रोग्रामिंग में सीधे कार्यात्मक इंटरफेस के रूप में भाग लेने से बाहर रखा गया है, (खराब) डिजाइन द्वारा। Class.forNamenames.forEach(Class::forName)
ईसाई हुजेर

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

13
@BrianGoetz क्या आपके द्वारा उल्लिखित अनिर्दिष्ट अंतर्ग्रहण समस्याओं पर कुछ सार्वजनिक जानकारी उपलब्ध है? मैं उत्सुक हूं और इसे समझना चाहूंगा।
ईसाई हुजेर

169

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

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 LambdaExceptionUtil {

@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; }

}

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

@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");
    }    

नोट 1:rethrow के तरीकों LambdaExceptionUtilसे ऊपर वर्ग डर के बिना इस्तेमाल किया जा सकता है, और कर रहे हैं किसी भी स्थिति में उपयोग करने के लिए ठीक । उपयोगकर्ता @PaoloC के लिए एक बड़ा धन्यवाद जिसने अंतिम समस्या को हल करने में मदद की: अब कंपाइलर आपको थ्रो क्लॉज़ और सब कुछ जोड़ने के लिए कहेंगे जैसे कि आप जावा 8 स्ट्रीम पर मूल रूप से अपवादों को चेक कर सकते हैं।


नोट 2:uncheck के तरीकों LambdaExceptionUtilसे ऊपर वर्ग बोनस तरीके हैं, और सुरक्षित रूप से उन्हें कक्षा से हटाया जा सकता आप उन्हें का उपयोग नहीं करना चाहते हैं। यदि आप उनका उपयोग करते हैं, तो इसे सावधानी से करें, और निम्नलिखित उपयोग के मामलों, फायदे / नुकसान और सीमाओं को समझने से पहले नहीं:

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

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

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


4
मुझे लगता है कि यह जवाब अन्यायपूर्ण था। कोड काम करता है। चेक किए गए अपवादों को फेंक दिया जाना चाहिए या उनसे निपटा जाना चाहिए। यदि आप उन्हें फेंकना चाहते हैं, तो "थ्रो क्लॉज" को उस विधि में रखें जिसमें धारा हो। लेकिन अगर आप केवल रैपिंग और रीथ्रोइंग द्वारा उनके साथ व्यवहार करना चाहते हैं, तो मुझे लगता है कि मैं अपवादों को "अनडेक" करने के लिए ऊपर दिए गए कोड का उपयोग करना पसंद करता हूं और उन्हें खुद से बुलबुला करने देता हूं। एकमात्र अंतर जो मुझे ज्ञात है कि बुदबुदाहट अपवाद RuntimeException का विस्तार नहीं करेगा। मुझे पता है कि शुद्धतावादी ऐसा नहीं करेंगे, लेकिन क्या यह "अनिवार्य रूप से किसी को काटने के लिए वापस आएगा"? संभावना नहीं लगती है।
मार्क जी

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

16
मैंने केवल इसलिए अपमानित किया क्योंकि इससे अचूक कोड को बढ़ावा मिलता है । यह एक बदसूरत हैक है, हालांकि एक चतुर एक है, और मुझे यह उत्तर उपयोगी कभी नहीं मिलेगा। यह है, फिर से, भाषा का एक और "उपयोग न करें"।
अनहाइड्रोन 20

12
@ यूनीहेड्रो लेकिन यह अकल्पनीय क्यों हो गया? मैं क्यों नहीं देख सकता। कोई उदाहरण?
मार्क जी

2
मेरी राय में @SuppressWarnings ("unchecked")संकलक प्रवंचना पूरी तरह से अस्वीकार्य है।
थोरबजोरन रावन एंडरसन

26

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

यहाँ यह करने के लिए थोड़ा सुरक्षित तरीका है (लेकिन मैं अभी भी यह अनुशंसा नहीं करते हैं।)

class WrappedException extends RuntimeException {
    Throwable cause;

    WrappedException(Throwable cause) { this.cause = cause; }
}

static WrappedException throwWrapped(Throwable t) {
    throw new WrappedException(t);
}

try 
    source.stream()
          .filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... })
          ...
}
catch (WrappedException w) {
    throw (IOException) w.cause;
}

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


18
सिर्फ एक प्रश्न; डिजाइन निर्णय क्या था जिसके कारण लंबोदर ने अपने संदर्भ से बाहर किए गए अपवादों का प्रचार नहीं किया? ध्यान दें कि मैं समझता हूं कि कार्यात्मक इंटरफेस Functionआदि जैसे throwsकुछ भी नहीं है; मैं सिर्फ उत्सुक हूँ।
18

4
यह throw w.cause;संकलक शिकायत नहीं करेगा कि विधि न तो फेंकता है और न ही पकड़ता है Throwable? इसलिए, यह संभावना है कि IOExceptionवहां एक कलाकार की जरूरत होगी। इसके अलावा, यदि लैम्ब्डा एक से अधिक प्रकार के चेक किए गए अपवादों को फेंकता है, तो चेक का बॉडी कुछ instanceofजाँचों (या इसी तरह के उद्देश्य के साथ कुछ और) के साथ बदसूरत हो जाता है, यह जाँचने के लिए कि कौन सा अपवाद फेंक दिया गया था।
विक्टर स्टैफुसा

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

2
@ न्यूडेटिकस मैं आपसे सहमत हूँ। कहा कि, क्या आप अधिक से अधिक रैपिंग करना पसंद करते हैं (जैसा कि ऊपर दिखाया गया है, "भूल जाना" का जोखिम बढ़ रहा है) या सिर्फ 4 चतुर इंटरफेस बनाएं और lambdas w / o रैपिंग का उपयोग करें, जैसा कि stackoverflow.com/a/30930991/2365724 में दिखाया गया है ? धन्यवाद
PaoloC

10
सच कहूँ तो, यह समाधान पूरी तरह से अस्थिर है। मुझे लगा कि धाराओं का बिंदु बॉयलरप्लेट को कम करना था, इसे बढ़ाना नहीं।
wvdz

24

आप ऐसा कर सकते हैं!

@Marcg का विस्तार करना UtilExceptionऔर throw Eजहां आवश्यक हो: इस तरह से, कंपाइलर आपको थ्रो क्लॉज़ और सब कुछ जोड़ने के लिए कहेगा जैसे कि आप जाव 8 की धाराओं पर मूल रूप से अपवादों को जांच सकते हैं।

निर्देश: बस LambdaExceptionUtilअपने आईडीई में कॉपी / पेस्ट करें और फिर नीचे दिखाए अनुसार इसका उपयोग करें LambdaExceptionUtilTest

public final class LambdaExceptionUtil {

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

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

    /**
     * .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name))));
     */
    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) {
                throwActualException(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) {
                throwActualException(exception);
                return null;
            }
        };
    }

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

}

उपयोग और व्यवहार दिखाने के लिए कुछ परीक्षण:

public class LambdaExceptionUtilTest {

    @Test(expected = MyTestException.class)
    public void testConsumer() throws MyTestException {
        Stream.of((String)null).forEach(rethrowConsumer(s -> checkValue(s)));
    }

    private void checkValue(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
    }

    private class MyTestException extends Exception { }

    @Test
    public void testConsumerRaisingExceptionInTheMiddle() {
        MyLongAccumulator accumulator = new MyLongAccumulator();
        try {
            Stream.of(2L, 3L, 4L, null, 5L).forEach(rethrowConsumer(s -> accumulator.add(s)));
            fail();
        } catch (MyTestException e) {
            assertEquals(9L, accumulator.acc);
        }
    }

    private class MyLongAccumulator {
        private long acc = 0;
        public void add(Long value) throws MyTestException {
            if(value==null) {
                throw new MyTestException();
            }
            acc += value;
        }
    }

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
        assertEquals(2, sizes.size());
        assertEquals(4, sizes.get(0).intValue());
        assertEquals(5, sizes.get(1).intValue());
    }

    private Integer transform(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
        return value.length();
    }

    @Test(expected = MyTestException.class)
    public void testFunctionRaisingException() throws MyTestException {
        Stream.of("ciao", null, "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
    }

}

1
क्षमा करें @setheron आप सही हैं, बस <Integer>पहले जोड़ें map। वास्तव में, जावा कंपाइलर Integerरिटर्न प्रकार का अनुमान नहीं लगा सकता है । बाकी सब सही होना चाहिए।
पाओलोके

1
इसने मेरे लिए काम किया। इसने अपवाद को लागू करने के द्वारा मार्कग के उत्तर को सही बनाया।
स्किचन

1
उपरोक्त समस्या का समाधान: इस उपभोक्ता की तरह परिवर्तनशील घोषित करें <ThingType> अभिव्यक्ति = rethrowConsumer ((ThingType thing) -> thing.clone ()); फिर उस अभिव्यक्ति को अंदर के फॉर्च्यूनर के अंदर इस्तेमाल करें।
स्किचन

1
@ स्किचन: इस संशोधित नए संस्करण के बाद से आप किसी भी अपवाद को दबा नहीं रहे हैं, यह शायद अनुमान प्रणाली के लिए थोड़ा अधिक कठिन है। नीचे कुछ टिप्पणी में ब्रायन गोएत्ज़ "अपवाद पारदर्शिता" के बारे में बात करते हैं, जिसके कारण "अनिर्णायक इंजेक्शन की समस्या" होती है।
मार्क जी

3
बहुत अच्छा। एकमात्र दुर्भाग्यपूर्ण बात यह है कि यह पूरी तरह से एक विधि के साथ काम नहीं करता है जो कई जांचे गए अपवादों को फेंक देता है। इस मामले में संकलक आपको एक सामान्य सुपर-टाइप को पकड़ लेगा, जैसे Exception
wvdz

12

बस NoException (मेरी परियोजना), jOOλ के अनियंत्रित , फेंकने वाले लैम्ब्डा , थ्रोएबल इंटरफेस या फॉक्स पास में से किसी एक का उपयोग करें ।

// NoException
stream.map(Exceptions.sneak().function(Class::forName));

// jOOλ
stream.map(Unchecked.function(Class::forName));

// throwing-lambdas
stream.map(Throwing.function(Class::forName).sneakyThrow());

// Throwable interfaces
stream.map(FunctionWithThrowable.aFunctionThatUnsafelyThrowsUnchecked(Class::forName));

// Faux Pas
stream.map(FauxPas.throwingFunction(Class::forName));

7

मैंने एक लाइब्रेरी लिखी है जो आपको स्ट्रीम अपवादों को फेंकने की अनुमति देने के लिए स्ट्रीम एपीआई का विस्तार करती है। इसमें ब्रायन गोएट्ज की ट्रिक का इस्तेमाल किया गया है।

आपका कोड बन जाएगा

public List<Class> getClasses() throws ClassNotFoundException {     
    Stream<String> classNames = 
        Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String");

    return ThrowingStream.of(classNames, ClassNotFoundException.class)
               .map(Class::forName)
               .collect(Collectors.toList());
}

7

यह उत्तर 17 के समान है लेकिन आवरण अपवाद की परिभाषा से बचना:

List test = new ArrayList();
        try {
            test.forEach(obj -> {

                //let say some functionality throws an exception
                try {
                    throw new IOException("test");
                }
                catch(Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }
        catch (RuntimeException re) {
            if(re.getCause() instanceof IOException) {
                //do your logic for catching checked
            }
            else 
                throw re; // it might be that there is real runtime exception
        }

1
यह एक सरल और प्रभावी उपाय है।
लिन डब्ल्यू

2
यह वही है जो ओप नहीं चाहता था: लैम्ब्डा में ब्लॉक का प्रयास करें। Futhermore, यह केवल तब तक काम करता है जब तक कि कोशिश ब्लॉक के बाहर कोई अन्य कोड एक ROOimeException में IOException को लपेटता है। इससे बचने के लिए, एक कस्टम रैपर-रनटाइम एक्ससेप्शन (एक निजी आंतरिक वर्ग के रूप में परिभाषित) का उपयोग किया जा सकता है।
माल्टे हार्टविग

5

आप नहीं कर सकते।

हालाँकि, आप मेरी एक परियोजना पर एक नज़र डालना चाहते हैं, जो आपको ऐसे "फेंकने वाले लंबोदा" को अधिक आसानी से हेरफेर करने की अनुमति देता है।

आपके मामले में, आप ऐसा करने में सक्षम होंगे:

import static com.github.fge.lambdas.functions.Functions.wrap;

final ThrowingFunction<String, Class<?>> f = wrap(Class::forName);

List<Class> classes =
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .map(f.orThrow(MyException.class))
          .collect(Collectors.toList());

और पकड़ MyException

वह एक उदाहरण है। एक अन्य उदाहरण यह है कि आप .orReturn()कुछ डिफ़ॉल्ट मान ले सकते हैं।

ध्यान दें कि यह अभी भी एक कार्य प्रगति पर है, और अधिक आना है। बेहतर नाम, अधिक सुविधाएँ आदि।


2
लेकिन फिर, यदि आप मूल चेक अपवाद को फेंकना चाहते हैं, तो आपको स्ट्रीम को पकड़ने / जोड़ने के लिए, इसे खोलना होगा, जो अभी भी भयानक है! मुझे यह विचार पसंद है कि यदि आप चाहते हैं, तो आप एक अनियंत्रित अपवाद को फेंक सकते हैं, और यदि आप चाहते हैं तो आप उस डिफ़ॉल्ट मान को स्ट्रीम में वापस कर देंगे, लेकिन मुझे यह भी लगता है कि आपको .orThrowChecked()अपने प्रोजेक्ट में कुछ विधि जोड़ना चाहिए जिससे चेक किए गए अपवाद स्वयं ही फेंक दिए जा सकें । कृपया UtilExceptionइस पृष्ठ पर मेरे उत्तर पर एक नज़र डालें , और देखें कि क्या आपको अपनी परियोजना में इस तीसरी संभावना को जोड़ने का विचार पसंद है।
मार्क जी

"लेकिन फिर, यदि आप मूल चेक अपवाद को फेंकना चाहते हैं, तो आपको स्ट्रीम को पकड़ने / जोड़ने के लिए, इसे खोलना होगा, जो अभी भी भयानक है!" <- हाँ लेकिन आपके पास कोई विकल्प नहीं है। लैंबड्स ने उनके संदर्भ से बाहर किए गए अपवादों का प्रचार नहीं किया है, यह एक डिजाइन "निर्णय" है (मैं इसे व्यक्तिगत रूप से एक दोष के रूप में देखता हूं, लेकिन ओहवेल)
17

आपके विचार के अनुसार, मैं इसका पालन नहीं करता, क्षमा करें; आखिरकार आप अभी भी अनियंत्रित होकर फेंक रहे हैं, तो मैं जो करता हूं, उससे यह कैसे अलग है? (सिवाय इसके कि मेरे पास इसके लिए एक अलग इंटरफ़ेस है)
18

वैसे भी, परियोजना में योगदान देने के लिए आपका स्वागत है! इसके अलावा, क्या आपने देखा है कि Streamऔजार AutoCloseable?
फेज

मुझे आपसे यह पूछना चाहिए: क्या आपके MyExceptionउपरोक्त को अनियंत्रित अपवाद होने की आवश्यकता है?
18

3

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

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .map(Try.<String, Class<?>>safe(Class::forName)
                  .handle(System.out::println)
                  .unsafe())
          .collect(toList());

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

कक्षा का प्रयास करें ग्राहक कोड के लिए अंत बिंदु है। प्रत्येक फ़ंक्शन प्रकार के लिए सुरक्षित विधियों में अद्वितीय नाम हो सकता है। CheckedConsumer , CheckedSupplier और CheckedFunction lib कार्यों के अनुरूप जो की स्वतंत्र रूप से इस्तेमाल किया जा सकता जाँच कर रहे हैं की कोशिश

CheckedBuilder कुछ चेक किए गए फ़ंक्शन में अपवादों को संभालने के लिए इंटरफ़ेस है। अगर पिछले असफल रहा तो orTry दूसरे प्रकार के फ़ंक्शन को निष्पादित करने की अनुमति देता है। हैंडल अपवाद प्रकार फ़िल्टरिंग सहित अपवाद हैंडलिंग प्रदान करता है। हैंडलर का क्रम महत्वपूर्ण है। कम करें तरीकों असुरक्षित और rethrow निष्पादन श्रृंखला में पिछले अपवाद rethrows। कम करें तरीकों orElse और orElseGet अगर सभी कार्यों में विफल रहा है वैकल्पिक लोगों की तरह वैकल्पिक मान। इसके अलावा विधि दमन हैCheckedWrapper CheckedBuilder का सामान्य कार्यान्वयन है।

final class Try {

    public static <T> CheckedBuilder<Supplier<T>, CheckedSupplier<T>, T> 
        safe(CheckedSupplier<T> supplier) {
        return new CheckedWrapper<>(supplier, 
                (current, next, handler, orResult) -> () -> {
            try { return current.get(); } catch (Exception ex) {
                handler.accept(ex);
                return next.isPresent() ? next.get().get() : orResult.apply(ex);
            }
        });
    }

    public static <T> Supplier<T> unsafe(CheckedSupplier<T> supplier) {
        return supplier;
    }

    public static <T> CheckedBuilder<Consumer<T>, CheckedConsumer<T>, Void> 
        safe(CheckedConsumer<T> consumer) {
        return new CheckedWrapper<>(consumer, 
                (current, next, handler, orResult) -> t -> {
            try { current.accept(t); } catch (Exception ex) {
                handler.accept(ex);
                if (next.isPresent()) {
                    next.get().accept(t);
                } else {
                    orResult.apply(ex);
                }
            }
        });
    }

    public static <T> Consumer<T> unsafe(CheckedConsumer<T> consumer) {
        return consumer;
    }

    public static <T, R> CheckedBuilder<Function<T, R>, CheckedFunction<T, R>, R> 
        safe(CheckedFunction<T, R> function) {
        return new CheckedWrapper<>(function, 
                (current, next, handler, orResult) -> t -> {
            try { return current.applyUnsafe(t); } catch (Exception ex) {
                handler.accept(ex);
                return next.isPresent() ? next.get().apply(t) : orResult.apply(ex);
            }
        });
    }

    public static <T, R> Function<T, R> unsafe(CheckedFunction<T, R> function) {
        return function;
    }

    @SuppressWarnings ("unchecked")
    static <T, E extends Throwable> T throwAsUnchecked(Throwable exception) throws E { 
        throw (E) exception; 
    }
}

@FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
    void acceptUnsafe(T t) throws Exception;
    @Override default void accept(T t) {
        try { acceptUnsafe(t); } catch (Exception ex) {
            Try.throwAsUnchecked(ex);
        }
    }
}

@FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
    R applyUnsafe(T t) throws Exception;
    @Override default R apply(T t) {
        try { return applyUnsafe(t); } catch (Exception ex) {
            return Try.throwAsUnchecked(ex);
        }
    }
}

@FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
    T getUnsafe() throws Exception;
    @Override default T get() {
        try { return getUnsafe(); } catch (Exception ex) {
            return Try.throwAsUnchecked(ex);
        }
    }
}

interface ReduceFunction<TSafe, TUnsafe, R> {
    TSafe wrap(TUnsafe current, Optional<TSafe> next, 
            Consumer<Throwable> handler, Function<Throwable, R> orResult);
}

interface CheckedBuilder<TSafe, TUnsafe, R> {
    CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next);

    CheckedBuilder<TSafe, TUnsafe, R> handle(Consumer<Throwable> handler);

    <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handle(
            Class<E> exceptionType, Consumer<E> handler);

    CheckedBuilder<TSafe, TUnsafe, R> handleLast(Consumer<Throwable> handler);

    <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handleLast(
            Class<E> exceptionType, Consumer<? super E> handler);

    TSafe unsafe();
    TSafe rethrow(Function<Throwable, Exception> transformer);
    TSafe suppress();
    TSafe orElse(R value);
    TSafe orElseGet(Supplier<R> valueProvider);
}

final class CheckedWrapper<TSafe, TUnsafe, R> 
        implements CheckedBuilder<TSafe, TUnsafe, R> {

    private final TUnsafe function;
    private final ReduceFunction<TSafe, TUnsafe, R> reduceFunction;

    private final CheckedWrapper<TSafe, TUnsafe, R> root;
    private CheckedWrapper<TSafe, TUnsafe, R> next;

    private Consumer<Throwable> handlers = ex -> { };
    private Consumer<Throwable> lastHandlers = ex -> { };

    CheckedWrapper(TUnsafe function, 
            ReduceFunction<TSafe, TUnsafe, R> reduceFunction) {
        this.function = function;
        this.reduceFunction = reduceFunction;
        this.root = this;
    }

    private CheckedWrapper(TUnsafe function, 
            CheckedWrapper<TSafe, TUnsafe, R> prev) {
        this.function = function;
        this.reduceFunction = prev.reduceFunction;
        this.root = prev.root;
        prev.next = this;
    }

    @Override public CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next) {
        return new CheckedWrapper<>(next, this);
    }

    @Override public CheckedBuilder<TSafe, TUnsafe, R> handle(
            Consumer<Throwable> handler) {
        handlers = handlers.andThen(handler);
        return this;
    }

    @Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> 
        handle(Class<E> exceptionType, Consumer<E> handler) {
        handlers = handlers.andThen(ex -> {
            if (exceptionType.isInstance(ex)) {
                handler.accept(exceptionType.cast(ex));
            }
        });
        return this;
    }

    @Override public CheckedBuilder<TSafe, TUnsafe, R> handleLast(
            Consumer<Throwable> handler) {
        lastHandlers = lastHandlers.andThen(handler);
        return this;
    }

    @Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> 
        handleLast(Class<E> exceptionType, Consumer<? super E> handler) {
        lastHandlers = lastHandlers.andThen(ex -> {
            if (exceptionType.isInstance(ex)) {
                handler.accept(exceptionType.cast(ex));
            }
        });
        return this;
    }

    @Override public TSafe unsafe() {
        return root.reduce(ex -> Try.throwAsUnchecked(ex));
    }

    @Override
    public TSafe rethrow(Function<Throwable, Exception> transformer) {
        return root.reduce(ex -> Try.throwAsUnchecked(transformer.apply(ex)));
    }

    @Override public TSafe suppress() {
        return root.reduce(ex -> null);
    }

    @Override public TSafe orElse(R value) {
        return root.reduce(ex -> value);
    }

    @Override public TSafe orElseGet(Supplier<R> valueProvider) {
        Objects.requireNonNull(valueProvider);
        return root.reduce(ex -> valueProvider.get());
    }

    private TSafe reduce(Function<Throwable, R> orResult) {
        return reduceFunction.wrap(function, 
                Optional.ofNullable(next).map(p -> p.reduce(orResult)), 
                this::handle, orResult);
    }

    private void handle(Throwable ex) {
        for (CheckedWrapper<TSafe, TUnsafe, R> current = this; 
                current != null; 
                current = current.next) {
            current.handlers.accept(ex);
        }
        lastHandlers.accept(ex);
    }
}

3

TL; DR बस लोम्बोक का उपयोग करें@SneakyThrows

ईसाई हुजेर ने पहले ही विस्तार से बताया है कि जावा की सीमाओं के कारण कड़ाई से जाँच किए गए अपवादों को फेंकना, सख्ती से बोलना संभव नहीं है।

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

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

परिणामस्वरूप कोड उतना ही सरल है:

public List<Class> getClasses() throws ClassNotFoundException {
    List<Class> classes =
        Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                .map(className -> getClass(className))
                .collect(Collectors.toList());
    return classes;
}

@SneakyThrows                                 // <= this is the only new code
private Class<?> getClass(String className) {
    return Class.forName(className);
}

हमें सिर्फ एक Extract Methodरिफैक्टरिंग (आईडीई द्वारा किया गया) और एक अतिरिक्त लाइन की आवश्यकता थी @SneakyThrows। एनोटेशन यह सुनिश्चित करने के लिए सभी बॉयलरप्लेट को जोड़ने का ख्याल रखता है कि आप अपने चेक किए गए अपवाद को बिना लपेटे RuntimeExceptionऔर बिना स्पष्ट रूप से घोषित किए बिना फेंक सकते हैं ।


4
लुम्बोक के उपयोग को हतोत्साहित किया जाना चाहिए।
ड्रैगस

2

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

private void run() {
    List<String> list = Stream.of(1, 2, 3, 4).map(wrapper(i ->
            String.valueOf(++i / 0), i -> String.valueOf(++i))).collect(Collectors.toList());
    System.out.println(list.toString());
}

private <T, R, E extends Exception> Function<T, R> wrapper(ThrowingFunction<T, R, E> function, 
Function<T, R> onException) {
    return i -> {
        try {
            return function.apply(i);
        } catch (ArithmeticException e) {
            System.out.println("Exception: " + i);
            return onException.apply(i);
        } catch (Exception e) {
            System.out.println("Other: " + i);
            return onException.apply(i);
        }
    };
}

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

2

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

    @Test
    public void getClasses() {

        String[] classNames = {"java.lang.Object", "java.lang.Integer", "java.lang.Foo"};
        List<Class> classes =
                Stream.of(classNames)
                        .map(className -> {
                            try {
                                return Class.forName(className);
                            } catch (ClassNotFoundException e) {
                                // log the error
                                return null;
                            }
                        })
                        .filter(c -> c != null)
                        .collect(Collectors.toList());

        if (classes.size() != classNames.length) {
            // add your error handling here if needed or process only the resulting list
            System.out.println("Did not process all class names");
        }

        classes.forEach(System.out::println);
    }

1

मैं उपरोक्त टिप्पणियों से सहमत हूं, Stream.map का उपयोग करके आप फ़ंक्शन को लागू करने तक सीमित हैं जो अपवादों को नहीं फेंकते हैं।

हालाँकि आप अपना खुद का कार्यात्मक इन्टरफेस बना सकते हैं जो नीचे दिया गया है।

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

फिर इसे नीचे दिखाए गए अनुसार लैंबडास या संदर्भों का उपयोग करके लागू करें।

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}

1

चेक किए गए अपवादों को संभालने का एकमात्र ऐसा तरीका है कि एक mapऑपरेशन द्वारा फेंका जा सकता है, उन्हें एक के भीतर इनकैप्सुलेट करना है CompletableFuture। ( Optionalयह एक सरल विकल्प है यदि आपको अपवाद को संरक्षित करने की आवश्यकता नहीं है।) इन वर्गों का उद्देश्य आपको कार्यात्मक तरीके से आकस्मिक संचालन का प्रतिनिधित्व करने की अनुमति देना है।

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

    CompletableFuture<List<Class<?>>> classes =
            Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double")
                  .map(MonadUtils.applyOrDie(Class::forName))
                  .map(cfc -> cfc.thenApply(Class::getSuperclass))
                  .collect(MonadUtils.cfCollector(ArrayList::new,
                                                  List::add,
                                                  (List<Class<?>> l1, List<Class<?>> l2) -> { l1.addAll(l2); return l1; },
                                                  x -> x));
    classes.thenAccept(System.out::println)
           .exceptionally(t -> { System.out.println("unable to get class: " + t); return null; });

यह निम्न आउटपुट का उत्पादन करता है:

[class java.lang.Object, class java.lang.Number, class java.lang.Number]

applyOrDieविधि एक लेता है Functionकि एक अपवाद फेंकता है, और यह धर्मान्तरित एक Functionहै कि रिटर्न पहले से पूरा CompletableFuture- या तो, मूल कार्य के परिणाम के साथ सामान्य रूप से पूरा किया है या फेंक दिया अपवाद के साथ असाधारण पूरा किया।

दूसरा mapऑपरेशन दिखाता है कि आपको अब Stream<CompletableFuture<T>>सिर्फ एक के बजाय मिल गया है Stream<T>CompletableFutureकेवल इस ऑपरेशन को निष्पादित करने का ख्याल रखता है अगर अपस्ट्रीम ऑपरेशन सफल रहा। एपीआई यह खोज करता है, लेकिन अपेक्षाकृत दर्द रहित है।

जब तक आप collectचरण के लिए नहीं मिलता है , वह है। यह वह जगह है जहाँ हमें एक बहुत महत्वपूर्ण सहायक विधि की आवश्यकता होती है। हम "उठाना" एक सामान्य संग्रह आपरेशन (इस मामले में, करना चाहते हैं toList()"अंदर") CompletableFuture- cfCollector()की सुविधा देता है हमें करना है कि का उपयोग कर एक supplier, accumulator, combiner, और finisherउस के बारे में सब पर कुछ भी पता करने की जरूरत नहीं CompletableFuture

हेल्पर तरीके मेरी MonadUtilsकक्षा में गीथहब पर पाए जा सकते हैं , जो अभी भी प्रगति पर है।


1

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

उदाहरण:

interface CheckedFunction<I, O> {
    O apply(I i) throws Exception; }

static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
    return i -> {
        try {
            return f.apply(i);
        } catch(Exception ex) {

            throw new RuntimeException(ex);
        }
    } }

fileNamesToRead.map(unchecked(file -> Files.readAllLines(file)))

या

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

static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
    return arg -> {
        try {
            return f.apply(arg);
        } catch(Exception ex) {
            return throwUnchecked(ex);
        }
    };
}

2 कार्यान्वयन एक में अपवाद को लपेटने से बचा जाता है RuntimeExceptionthrowUncheckedकाम करता है क्योंकि लगभग हमेशा सभी सामान्य अपवादों को जावा में अनियंत्रित माना जाता है।


1

मैं इस तरह के रैपिंग अपवाद का उपयोग करता हूं:

public class CheckedExceptionWrapper extends RuntimeException {
    ...
    public <T extends Exception> CheckedExceptionWrapper rethrow() throws T {
        throw (T) getCause();
    }
}

इसे इन अपवादों को सांख्यिकीय रूप से संभालने की आवश्यकता होगी:

void method() throws IOException, ServletException {
    try { 
        list.stream().forEach(object -> {
            ...
            throw new CheckedExceptionWrapper(e);
            ...            
        });
    } catch (CheckedExceptionWrapper e){
        e.<IOException>rethrow();
        e.<ServletExcepion>rethrow();
    }
}

इसे ऑनलाइन आज़माएं!

हालांकि अपवाद को पहले rethrow()कॉल (ओह, जावा जेनरिक ...) के दौरान फिर से फेंक दिया जाएगा , इस तरह से संभव अपवादों की एक सख्त सांख्यिकीय परिभाषा प्राप्त करने की अनुमति मिलती है (उन्हें घोषित करने की आवश्यकता होती है throws)। और न instanceofकुछ चाहिए।


-1

मुझे लगता है कि यह दृष्टिकोण सही है:

public List<Class> getClasses() throws ClassNotFoundException {
    List<Class> classes;
    try {
        classes = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String").map(className -> {
            try {
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new UndeclaredThrowableException(e);
            }
        }).collect(Collectors.toList());
    } catch (UndeclaredThrowableException e) {
        if (e.getCause() instanceof ClassNotFoundException) {
            throw (ClassNotFoundException) e.getCause();
        } else {
            // this should never happen
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
    return classes;
}

CallableUndeclaredThrowableException(जो कि इस अपवाद के लिए उपयोग का मामला है) के अंदर चेक किए गए अपवाद को लपेटकर और इसे बाहर कर देना।

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

जैसा कि कई अन्य लोगों ने बताया है, इस स्थिति के समाधान हैं, और मुझे आशा है कि उनमें से एक इसे जावा के भविष्य के संस्करण में बना देगा।


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