java - जावा 8 लैम्ब्डा फ़ंक्शन जो अपवाद फेंकता है?




lambda java-8 (16)

आप अस्थिर रैपर का उपयोग कर सकते हैं

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

या

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);

मुझे पता है कि एक स्ट्रिंग पैरामीटर वाले एक विधि का संदर्भ कैसे बनाया जाए और एक int लौटाए, यह है:

Function<String, Integer>

हालांकि, यह कार्य नहीं करता है अगर फ़ंक्शन अपवाद फेंकता है, तो कहें कि इसे परिभाषित किया गया है:

Integer myMethod(String s) throws IOException

मैं इस संदर्भ को कैसे परिभाषित करूं?


आप इसके लिए ET उपयोग कर सकते हैं। अपवाद रूपांतरण / अनुवाद के लिए ईटी एक छोटी जावा 8 लाइब्रेरी है।

ईटी के साथ ऐसा लगता है:

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslator उदाहरण थ्रेड सुरक्षित हैं जिन्हें एकाधिक घटकों द्वारा साझा किया जा सकता है। यदि आप चाहें तो आप अधिक विशिष्ट अपवाद रूपांतरण नियम (जैसे FooCheckedException -> BarRuntimeException ) को कॉन्फ़िगर कर सकते हैं। यदि कोई अन्य नियम उपलब्ध नहीं है, तो चेक अपवाद स्वचालित रूप से RuntimeException परिवर्तित हो जाते हैं।

(अस्वीकरण: मैं ईटी का लेखक हूं)


आप वास्तव में Consumer (और Function इत्यादि) को एक नए इंटरफ़ेस के साथ बढ़ा सकते हैं जो अपवादों को संभालता है - जावा 8 के डिफ़ॉल्ट तरीकों का उपयोग करके!

इस इंटरफ़ेस पर विचार करें ( Consumer बढ़ाता है):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

फिर, उदाहरण के लिए, यदि आपके पास एक सूची है:

final List<String> list = Arrays.asList("A", "B", "C");

यदि आप अपवादों को फेंकने वाले कुछ कोड के साथ इसका उपभोग करना चाहते हैं (उदाहरण के लिए। प्रत्येक के साथ), तो पारंपरिक रूप से आप कोशिश / पकड़ ब्लॉक स्थापित कर लेंगे:

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

लेकिन इस नए इंटरफ़ेस के साथ, आप इसे लैम्ब्डा अभिव्यक्ति के साथ तुरंत चालू कर सकते हैं और संकलक शिकायत नहीं करेगा:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

या यहां तक ​​कि इसे और अधिक संक्षेप में डाला !:

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

अपडेट : ऐसा लगता है कि Durian नामक Durian का एक बहुत अच्छा उपयोगिता लाइब्रेरी हिस्सा है जिसका उपयोग इस समस्या को बहुत अधिक लचीलापन के साथ हल करने के लिए किया जा सकता है। उदाहरण के लिए, ऊपर दिए गए मेरे कार्यान्वयन में मैंने स्पष्ट रूप से त्रुटि प्रबंधन नीति ( System.out... या throw RuntimeException ) throw RuntimeException , जबकि डुरियन की त्रुटियां आपको उपयोगिता विधियों के एक बड़े सूट के माध्यम से फ्लाई पर नीति लागू करने की अनुमति देती हैं। इसे साझा करने के लिए धन्यवाद, @NedTwigg !.

नमूना उपयोग:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));

आपको निम्न में से एक करना होगा।

  • यदि यह आपका कोड है, तो अपने स्वयं के कार्यात्मक इंटरफ़ेस को परिभाषित करें जो चेक अपवाद की घोषणा करता है

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }
    

    और इसका इस्तेमाल करें

    void foo (CheckedFunction f) { ... }
    
  • अन्यथा, एक विधि में Integer myMethod(String s) को लपेटें जो चेक अपवाद घोषित नहीं करता है:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    

    और फिर

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);
    

    या

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    

डिफ़ॉल्ट रूप से, जावा 8 Function अपवाद फेंकने की अनुमति नहीं देता है और जैसा कि कई उत्तरों में सुझाया गया है, इसे प्राप्त करने के कई तरीके हैं, एक तरीका यह है:

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

इस रूप में परिभाषित:

private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
    if ("abc".equals(str)) {
        throw new IOException();
    }
  return 1;
};

और कॉलर विधि में उसी अपवाद को throws या try/catch


प्रस्तावित समाधानों में से कई ने ई के सामान्य तर्क का उपयोग अपवाद के प्रकार में पारित करने के लिए किया है जो फेंक दिया जाता है।

उस चरण को आगे ले जाएं, और अपवाद के प्रकार में गुजरने के बजाय, अपवाद के प्रकार के उपभोक्ता में पास करें, जैसे ...

Consumer<E extends Exception>

आप Consumer<Exception> कई पुनः उपयोग करने योग्य विविधताएं बना सकते हैं जो आपके आवेदन की सामान्य अपवाद हैंडलिंग आवश्यकताओं को कवर करेंगे।


मुझे क्लास.forनाम और क्लास के साथ यह समस्या थी। लैम्ब्डा के अंदर नया इंस्टेंस, इसलिए मैंने अभी किया:

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

क्लास .forName ("myClass") को कॉल करने के बजाए लैम्ब्डा के अंदर। नया इंस्टेंस () मैंने अभी अनचेक किया हैनइंस्टेंसफोरनाम ("myClass")


मुझे लगता है कि Errors उपरोक्त विभिन्न सुझावों के कई पेशेवरों को जोड़ती है।

अपनी परियोजना में डुरियन को शामिल करने के लिए, आप या तो कर सकते हैं:

  • jcenter पर jcenter या maven central से इसे पकड़ो com.diffplug.durian:durian:3.3.0
  • या बस अपने कोड में केवल दो छोटी कक्षाओं को पेस्ट करें: Throwing.java और Errors.java

मैं जो कर रहा हूं वह उपयोगकर्ता को उस मूल्य को देने की अनुमति देना है जो वह वास्तव में अपवाद के मामले में चाहता है। तो मुझे ऐसा कुछ दिख रहा है

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

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

और फिर यह कॉल किया जा सकता है:

defaultIfThrows(child -> child.getID(), null)

यदि आपको किसी तृतीय पक्ष lib ( Vavr ) का उपयोग करने में कोई फर्क नहीं पड़ता है तो आप लिख सकते हैं

CheckedFunction1<String, Integer> f = this::myMethod;

इसमें तथाकथित कोशिश मोनैड भी है जो त्रुटियों को संभालता है:

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

कृपया here और पढ़ें।

अस्वीकरण: मैं वावर का निर्माता हूं।


यह जावा 8 के लिए विशिष्ट नहीं है। आप कुछ समकक्ष संकलित करने की कोशिश कर रहे हैं:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}

यह समस्या मुझे भी परेशान कर रही है; यही कारण है कि मैंने इस परियोजना को बनाया है।

इसके साथ आप कर सकते हैं:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

जेडीके द्वारा परिभाषित 39 इंटरफेस का एक टोटल है जिसमें इस तरह के Throwing समकक्ष हैं; वे सभी @FunctionalInterface धाराओं में उपयोग किए जाते हैं (बेस Stream लेकिन IntStream , LongStream और LongStream )।

और चूंकि उनमें से प्रत्येक अपने गैर फेंकने वाले समकक्ष का विस्तार करता है, आप सीधे उन्हें लैम्ब्स में भी उपयोग कर सकते हैं:

myStringStream.map(f) // <-- works

डिफ़ॉल्ट व्यवहार यह है कि जब आपका फेंकने वाला लैम्ब्डा एक चेक अपवाद फेंकता है, तो ThrownByLambdaException को कारण के रूप में चेक अपवाद के साथ फेंक दिया जाता है। इसलिए आप इसे पकड़ सकते हैं और कारण प्राप्त कर सकते हैं।

अन्य सुविधाएं भी उपलब्ध हैं।


हालांकि आप अपना खुद का FunctionalInterface बना सकते हैं जो नीचे के रूप में फेंकता है ..

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

फिर नीचे दिखाए गए अनुसार Lambdas या संदर्भों का उपयोग करके इसे कार्यान्वित करें।

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

 }

}

स्नीकी फेंक मुहावरे लैम्ब्डा अभिव्यक्ति की जांच किए गए अपवाद को बाईपास करने में सक्षम बनाता है। RuntimeException में एक चेकड अपवाद को लपेटना सख्त त्रुटि प्रबंधन के लिए अच्छा नहीं है।

इसका उपयोग जावा संग्रह में उपयोग किए जाने वाले Consumer फ़ंक्शन के रूप में किया जा सकता है।

यहां जिब के जवाब का एक सरल और बेहतर संस्करण है।

import static Throwing.rethrow;

@Test
public void testRethrow() {
    thrown.expect(IOException.class);
    thrown.expectMessage("i=3");

    Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
        int i = e.intValue();
        if (i == 3) {
            throw new IOException("i=" + i);
        }
    }));
}

बस लथड़ा में lambda लपेटो। यह चेकड एक्सेप्शन फेंक दिया जाता है जो लैम्ब्डा में हुआ था।

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
        return consumer;
    }

    /**
     * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
     * allows the unchecked exception to propagate.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

https://gist.github.com/myui/9722c1301434a3b69cf898ccd9090ff1#file-throwingtest-java-L40 में एक पूर्ण कोड और यूनिट परीक्षण खोजें


अस्वीकरण: मैंने अभी तक जावा 8 का उपयोग नहीं किया है, केवल इसके बारे में पढ़ें।

Function<String, Integer> IOException फेंक नहीं IOException , इसलिए आप IOException फेंकने IOException किसी भी कोड को नहीं डाल सकते हैं। यदि आप एक ऐसी विधि को कॉल कर रहे हैं जो Function<String, Integer> अपेक्षा करता है, तो उस विधि को पार करने वाले IOException , अवधि को फेंक नहीं सकते हैं। आप या तो इस तरह एक लैम्ब्डा लिख ​​सकते हैं (मुझे लगता है कि यह लैम्ब्डा वाक्यविन्यास है, यकीन नहीं है):

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

या, यदि आप जिस लैबडा को पास कर रहे हैं वह वह है जिसे आपने स्वयं लिखा है, तो आप एक नया कार्यात्मक इंटरफ़ेस परिभाषित कर सकते हैं और Function<String, Integer> बजाय पैरामीटर प्रकार के रूप में उपयोग कर सकते हैं:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}

public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}




java-8