Java Lambda funkcja, która generuje wyjątek?

469

Wiem, jak utworzyć odwołanie do metody, która ma Stringparametr i zwraca int:

Function<String, Integer>

Nie działa to jednak, jeśli funkcja zgłasza wyjątek, powiedzmy, że jest zdefiniowana jako:

Integer myMethod(String s) throws IOException

Jak zdefiniowałbym to odniesienie?

Triton Man
źródło
1
Powiązane: stackoverflow.com/questions/31637892/...
Marko Topolnik
4
Całe rozwiązanie wygląda trochę jak, rzucając wyjątki Runtime, uważam, że nie jest to dobre rozwiązanie. więc lepiej używać starej javy do pętli
Nazeel
5
Co z biblioteką jool ? por. org.jooq.lambda. Niesprawdzony pakiet
chaiyachaiya

Odpowiedzi:

402

Musisz wykonać jedną z następujących czynności.

  • Jeśli to twój kod, zdefiniuj własny interfejs funkcjonalny, który deklaruje sprawdzony wyjątek:

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

    i użyj go:

    void foo (CheckedFunction f) { ... }
  • W przeciwnym razie zawiń Integer myMethod(String s)metodę, która nie deklaruje sprawdzonego wyjątku:

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

    i wtedy:

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

    lub:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
Jason
źródło
7
Możesz faktycznie rozszerzyć Consumerlub Functionjeśli używasz metod domyślnych - zobacz moją odpowiedź poniżej.
jlb
2
Myślę, że można to osiągnąć jako jedno-liniowy .
Ned Twigg
6
Niewielka optymalizacja: Zamiast tego można również użyć (String t) -> myWrappedMethod(t)odwołania do metody this::myWrappedMethod.
Clashsoft,
8
Jeszcze bardziej ogólnym sposobem jest zdefiniowanie sprawdzonej funkcji, takiej jak ten publiczny interfejs @ Funkcjonalny interfejs Interfejs CheckedFunction <T, R, E rozszerza wyjątek> {R Apply (T t) rzuca E; } W ten sposób możesz również zdefiniować, który wyjątek zgłasza funkcja i możesz ponownie użyć interfejsu dla dowolnego kodu.
Martin Odhelius
3
Łał. Java jest gorsza niż myślałem
user275801
194

Możesz faktycznie rozszerzyć Consumer(i Functionitp.) Za pomocą nowego interfejsu, który obsługuje wyjątki - używając domyślnych metod Java 8 !

Rozważ ten interfejs (rozszerza 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;

}

Na przykład, jeśli masz listę:

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

Jeśli chcesz go skonsumować (np. Z forEach) kodem, który generuje wyjątki, tradycyjnie skonfigurowałbyś blok try / catch:

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

Ale dzięki temu nowemu interfejsowi możesz utworzyć instancję z wyrażeniem lambda, a kompilator nie będzie narzekał:

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

Lub nawet po prostu rzuć to bardziej zwięźle !:

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

Aktualizacja : Wygląda na to, że to bardzo miły część biblioteki użyteczność Durian zwane błędy , które mogą być wykorzystane, aby rozwiązać ten problem z elastycznością wiele więcej. Na przykład w powyższej implementacji wyraźnie zdefiniowałem zasady obsługi błędów ( System.out...lub throw RuntimeException), podczas gdy błędy Duriana pozwalają na stosowanie zasad w locie za pomocą dużego zestawu metod narzędziowych. Dzięki za udostępnienie , @NedTwigg !.

Przykładowe użycie:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
jlb
źródło
14
Masz więc zestaw interfejsów (funkcja, konsument, dostawca, ...) i zestaw zasad postępowania z błędami (rzucanie, System.out.println, ...). Myślę, że istnieje sposób na ułatwienie korzystania z dowolnej polityki z dowolnym rodzajem funkcji, bez konieczności kopiowania wklejania „ThrowingConsumer, ThrowingFunction itp.”.
Ned Twigg
1
jakiś czas później ... Postanowiłem użyć niesprawdzonych wyjątków i nie używać żadnych dodatkowych interfejsów funkcjonalnych ani nowych bibliotek -> łatwa droga, mniej pisania, szybsze dostarczanie, prawda?
aliopi,
1
Oto ulepszona wersja wykorzystująca podstępny idiom rzucania. Nie trzeba rozpakowywać wyjątku RuntimeException w CheckException.
myui
61

Myślę, że klasa DurianaErrors łączy wiele zalet różnych sugestii powyżej.

Aby włączyć Durian do swojego projektu, możesz:

Ned Twigg
źródło
Lub możesz po prostu użyć RxJava, ponieważ strumienie wymagają nieodłącznej obsługi błędów, a jeśli w twoim potoku jest coś, co rzuca wyjątek, istnieje duża szansa, że ​​jest to prawdopodobnie możliwy do zaobserwowania strumień. Nie wymusza to również Java 8 na dalszych użytkownikach biblioteki.
Adam Gent
2
Należy pamiętać, że Durian nie ma żadnych nowych wersji od czerwca 2016 r. Nie jest to korek pokazowy, ale o czym należy pamiętać.
Istvan Devai
9
Opiekun Durian tutaj. Co jest zepsute Jeśli użytkownik znajdzie błąd lub ważną brakującą funkcję, szybko opublikujemy poprawkę. Biblioteka jest prosta, dlatego nie mieliśmy żadnych raportów o błędach, więc nie musieliśmy publikować żadnych poprawek.
Ned Twigg
28

Nie dotyczy to Java 8. Próbujesz skompilować coś równoważnego do:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}
assylias
źródło
15
Pytanie brzmi: „Jak zdefiniowałbym to odniesienie?” . To tak naprawdę nie odpowiada na pytanie; wyjaśnia tylko, na czym polega problem.
Dawood ibn Kareem
13

Oświadczenie: Nie korzystałem jeszcze z Java 8, tylko o tym poczytałem.

Function<String, Integer>nie rzuca IOException, więc nie możesz w to wstawić żadnego kodu throws IOException. Jeśli wywołujesz metodę, która oczekuje a Function<String, Integer>, to lambda, którą przekazujesz do tej metody, nie może rzucać IOExceptionkropką. Możesz albo napisać lambda w ten sposób (myślę, że to jest składnia lambda, nie jestem pewien):

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

Lub, jeśli metoda, którą przekazujesz lambda, jest tą, którą sam napisałeś, możesz zdefiniować nowy interfejs funkcjonalny i użyć go jako typu parametru zamiast Function<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}
Adam R. Nelson
źródło
dodaj adnotację @FunctionalInterface przed interfejsem, tylko wtedy będzie można go używać w lambdach.
Gangnus,
13
@Gangnus: @FunctionalInterfaceadnotacja nie jest wymagana, aby można ją było wykorzystać w lambdas. Jest to jednak zalecane do kontroli poczytalności.
Tanmay Patil
9

Jeśli nie masz nic przeciwko korzystaniu z biblioteki innej firmy ( Vavr ), możesz napisać

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

Ma również tak zwaną Try monad, która obsługuje błędy:

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

Przeczytaj więcej tutaj .

Oświadczenie: Jestem twórcą Vavr.

Daniel Dietrich
źródło
7

Możesz użyć narzędzia „Unhrow wrapper”

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

lub

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
SeregaLBN
źródło
6

Możesz jednak stworzyć swój własny interfejs FunctionalInterface, który będzie wyrzucany jak poniżej.

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

następnie zaimplementuj go za pomocą Lambdas lub odnośników, jak pokazano poniżej.

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

 }

}
JohnnyO
źródło
6

Możesz.

Rozszerzanie @marcg UtilExceptioni dodawanie ogólnych <E extends Exception>tam, gdzie to konieczne: w ten sposób kompilator zmusi cię ponownie do dodania klauzul rzucania i wszystko będzie tak, jakbyś mógł natywnie sprawdzać wyjątki natywnie w strumieniach Java 8.

public final class LambdaExceptionUtil {

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

    /**
     * .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
    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();
    }

    private static class MyTestException extends Exception { }
}
PaoloC
źródło
5

Miałem ten problem z Class.forName i Class.newInstance wewnątrz lambda, więc po prostu:

public Object uncheckedNewInstanceForName (String name) {

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

Wewnątrz lambdy zamiast wywoływać Class.forName („myClass”). NewInstance () Właśnie wywołałem uncheckedNewInstanceForName („myClass”)

Sergio
źródło
4

Innym rozwiązaniem wykorzystującym opakowanie funkcji byłoby zwrócenie albo wystąpienia opakowania wyniku, powiedzmy Sukces, jeśli wszystko poszło dobrze, albo wystąpienia, powiedzmy Niepowodzenie.

Niektóre kody do wyjaśnienia rzeczy:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

Prosty przypadek użycia:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());
yohan
źródło
4

Ten problem również mnie niepokoi; dlatego stworzyłem ten projekt .

Dzięki niemu możesz:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Istnieje łącznie 39 interfejsów zdefiniowanych przez JDK, które mają taki Throwingodpowiednik; te są @FunctionalInterfacey stosowane w strumieniach (podkład Stream, ale także IntStream, LongStreami DoubleStream).

A ponieważ każdy z nich rozszerza swój nie rzucający odpowiednik, możesz bezpośrednio używać go również w lambdach:

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

Domyślne zachowanie polega na tym, że gdy twoja lambda rzuca sprawdzony wyjątek, ThrownByLambdaExceptiongenerowany jest a, którego przyczyną jest sprawdzony wyjątek. Możesz więc to uchwycić i uzyskać przyczynę.

Dostępne są również inne funkcje.

fge
źródło
Naprawdę podoba mi się ten pomysł, chciałbym tylko, abyście stworzyli rzuty ogólne, jak to sugerowano tutaj: javaspecialists.eu/archive/Issue221.html , np .: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- w ten sposób użytkownik nie musi wychwytywaćThrowable , ale zamiast tego sprawdził wyjątek.
Zoltán
@ Zoltán byłoby jednak utrudnieniem deklarowanie wyjątku za każdym razem; Ponadto zawsze możesz użyć, powiedzmy, .apply () zamiast .doApply () i złapać ThrownByLambdaException, będziesz mieć oryginalny wyjątek jako przyczynę (lub możesz użyć rethrow(...).as(MyRuntimeException.class))
fge
Myślę, że jest (w pewnym sensie ) sposób na obejście tego .
Ned Twigg
@NedTwigg Rozwiązałem to również dawno temu; Mogę teraz używać Throwing.runnable()innych i zawsze z możliwościami łączenia
fge
Funkcja łączenia jest bardzo fajna! Mój komentarz dotyczył tego, czy ThrowingRunnable powinien mieć ogólny wyjątek, czy nie. Zoltan zapytał, czy twoja biblioteka może mieć argument jako ogólny parametr, a ty odpowiedziałeś, że użycie tego byłoby uciążliwe. Mój link prowadził do niektórych wierszy kodu, które pokazują, że wyjątki mogą być ogólne, bez uciążliwości. Chyba że źle to odczytam, wyjątki w twojej bibliotece nie są ogólne (co jest rozsądnym wyborem projektowym, ponieważ nie zyskujesz dużej użyteczności, czyniąc je rodzajowymi).
Ned Twigg
4

Istnieje już wiele świetnych odpowiedzi tutaj. Próbuję rozwiązać problem z innej perspektywy. To tylko moje 2 centy, proszę mnie poprawić, jeśli się mylę.

Klauzula Throws w FunctionalInterface nie jest dobrym pomysłem

Myślę, że prawdopodobnie nie jest dobrym pomysłem egzekwowanie wyjątków IOException z następujących powodów

  • Wygląda mi to na anty-wzór dla Stream / Lambda. Cały pomysł polega na tym, że osoba dzwoniąca decyduje, jaki kod podać i jak obsłużyć wyjątek. W wielu scenariuszach wyjątek IOEx może nie mieć zastosowania do klienta. Na przykład, jeśli klient otrzymuje wartość z pamięci podręcznej / pamięci zamiast wykonywać rzeczywiste operacje we / wy.

  • Ponadto obsługa wyjątków w strumieniach staje się naprawdę ohydna. Na przykład, oto mój kod będzie wyglądał, jeśli użyję twojego API

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });

    Brzydka prawda? Ponadto, jak wspomniałem w pierwszym punkcie, że metoda doSomeOperation może generować wyjątek IOException (w zależności od implementacji klienta / wywołującego), ale z powodu klauzuli throws w metodzie FunctionalInterface zawsze muszę napisać próbuj złapać.

Co mam zrobić, jeśli naprawdę wiem, że ten interfejs API zgłasza wyjątek IOException

  • Prawdopodobnie mylimy interfejs Functional z typowymi interfejsami. Jeśli wiesz, że ten interfejs API wyrzuci wyjątek IOException, najprawdopodobniej znasz również pewne domyślne / abstrakcyjne zachowanie. Myślę, że powinieneś zdefiniować interfejs i wdrożyć bibliotekę (z domyślną / abstrakcyjną implementacją) w następujący sposób

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }

    Jednak nadal występuje problem z try-catch dla klienta. Jeśli używam interfejsu API w strumieniu, nadal muszę obsługiwać wyjątek IOExou w ohydnym bloku try-catch.

  • Podaj domyślny przyjazny interfejs API w następujący sposób

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }

    Domyślna metoda przyjmuje obiekt konsumenta jako argument, który będzie odpowiedzialny za obsługę wyjątku. Teraz, z punktu widzenia klienta, kod będzie wyglądał następująco

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());

    Fajnie prawda? Oczywiście zamiast Exception :: printStackTrace można zastosować logger lub inną logikę obsługi.

  • Możesz także udostępnić metodę podobną do https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Oznacza to, że możesz udostępnić inną metodę, która będzie zawierać wyjątek od poprzedniego wywołania metody. Wadą jest to, że teraz ustawiasz interfejsy API w stan, co oznacza, że ​​musisz zająć się bezpieczeństwem wątków i które ostatecznie stanie się hitem wydajności. Tylko opcja do rozważenia.

TriCore
źródło
Zgadzam się, że przekształcenie zaznaczonego wyjątku w niesprawdzony wyjątek lub połknięcie wyjątku nie jest dobrym pomysłem, ponieważ nie ma sposobu, aby dowiedzieć się, który element Streamzgłoszonego wyjątku. Dlatego podoba mi się pomysł obsługi wyjątku i filtrowania niepoprawnych wyników. Zauważ, że MyAmazingAPI jest w rzeczywistości FunctionalInterface(dlatego możesz dodać adnotację @FunctionalInterface). Możesz także mieć wartość domyślną zamiast używać Optional.empty().
Julien Kronegg
4

Podstępny idiom rzutu umożliwia ominięcie CheckedExceptionwyrażenia Lambda. Zawijanie CheckedExceptionw RuntimeExceptionnie jest dobre dla ścisłej obsługi błędów.

Może być używany jako Consumerfunkcja używana w kolekcji Java.

Oto prosta i ulepszona wersja odpowiedzi wysięgnika .

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

To właśnie Wrapps lambda w rethrow . Powoduje to CheckedExceptionrzucenie każdego, Exceptionco zostało wrzucone do twojej 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;
    }

}

Znajdź pełny kod i testy jednostkowe tutaj .

myui
źródło
3

Możesz do tego użyć ET . ET to mała biblioteka Java 8 do konwersji / tłumaczenia wyjątków.

Z ET wygląda to tak:

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

ExceptionTranslatorinstancje są bezpieczne dla wątków i mogą być współużytkowane przez wiele komponentów. Możesz skonfigurować bardziej szczegółowe reguły konwersji wyjątków (np. FooCheckedException -> BarRuntimeException), Jeśli chcesz. Jeśli żadne inne reguły nie są dostępne, zaznaczone wyjątki są automatycznie konwertowane na RuntimeException.

(Uwaga: Jestem autorem ET)

Micha
źródło
2
Wygląda na to, że jesteś autorem tej biblioteki. Według przepisów, tak , to musi ujawnić swoją przynależność w swoich odpowiedziach. Dodaj wyraźnie do swojej odpowiedzi, że napisałeś tę bibliotekę (to samo w przypadku innych odpowiedzi związanych z ET).
Tagir Valeev
2
Cześć Tagir, dzięki za podpowiedź. Zaktualizowałem odpowiedź.
micha
2

To, co robię, polega na umożliwieniu użytkownikowi podania wartości, której faktycznie chce w przypadku wyjątku. Więc mam coś takiego

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

Można to nazwać tak:

defaultIfThrows(child -> child.getID(), null)
mmounirou
źródło
1
Jest to rozszerzenie tego pomysłu, które wprowadza rozróżnienie między strategią „wartości domyślnej” (jak w twojej odpowiedzi) a strategią „przywracania wyjątku RuntimeException”, w której wartość domyślna nie jest konieczna.
Ned Twigg
2

Jeśli nie masz nic przeciwko korzystaniu z biblioteki strony trzeciej, z biblioteką cyclops- reag , biblioteką, w której pracuję, możesz użyć FluentFunctions interfejsu API do pisania

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

Funkcja ofChecked przyjmuje funkcję CheckedFunkcja jOOλ i zwraca zmiękczoną referencję do standardowej (niezaznaczonej) funkcji JDK java.util.function.Funkcja.

Alternatywnie możesz kontynuować pracę z przechwyconą funkcją za pomocą interfejsu FluentFunctions!

Na przykład, aby wykonać metodę, ponów ją do 5 razy i zapisz jej status, który możesz napisać

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");
John McClean
źródło
2

Domyślnie funkcja Java 8 nie zezwala na zgłaszanie wyjątku, a jak sugerowano w wielu odpowiedziach, istnieje wiele sposobów jego osiągnięcia, jednym ze sposobów jest:

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

Zdefiniuj jako:

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

I dodaj throwslub try/catchten sam wyjątek w metodzie dzwoniącego.

Arpit Aggarwal
źródło
2

Utwórz niestandardowy typ zwrotu, który będzie propagował sprawdzony wyjątek. Jest to alternatywa dla tworzenia nowego interfejsu, który odzwierciedla istniejący interfejs funkcjonalny z niewielką modyfikacją „zgłasza wyjątek” w metodzie interfejsu funkcjonalnego.

Definicja

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Stosowanie

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

Co się dzieje?

Tworzony jest pojedynczy interfejs funkcjonalny, który zgłasza sprawdzony wyjątek ( CheckedValueSupplier). Będzie to jedyny funkcjonalny interfejs, który pozwala na sprawdzanie wyjątków. Wszystkie inne interfejsy funkcjonalne wykorzystają CheckedValueSupplierdo zawinięcia dowolnego kodu, który zgłasza sprawdzony wyjątek.

CheckedValueKlasa odbędzie wyniku wykonywania jakiejkolwiek logiki, która rzuca sprawdzonej wyjątek. Zapobiega to propagacji sprawdzonego wyjątku do momentu, w którym kod będzie próbował uzyskać dostęp do wartości, którą zawiera instancja CheckedValue.

Problemy z tym podejściem.

  • Obecnie rzucamy „wyjątek”, skutecznie ukrywając pierwotnie rzucony typ.
  • Nie jesteśmy świadomi, że wystąpił wyjątek, dopóki nie CheckedValue#get()zostanie wywołany.

Consumer i in

Niektóre interfejsy funkcjonalne (Consumer na przykład) muszą być obsługiwane w inny sposób, ponieważ nie zapewniają wartości zwracanej.

Funkcja zamiast konsumenta

Jednym z podejść jest użycie funkcji zamiast konsumenta, co ma zastosowanie podczas obsługi strumieni.

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

Zwiększać

Alternatywnie, zawsze możesz eskalować do RuntimeException. Istnieją inne odpowiedzi, które obejmują eskalację sprawdzonego wyjątku z poziomu Consumer.

Nie spożywaj

Po prostu unikaj funkcjonalnych interfejsów i używaj dobrze zaprojektowanej pętli for.

justin.hughey
źródło
2

Używam przeciążonej funkcji narzędzia o nazwie, unchecked()która obsługuje wiele przypadków użycia.


NIEKTÓRE PRZYKŁADOWE ZASTOSOWANIA

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

NARZĘDZIA WSPIERAJĄCE

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
Stephen Paul
źródło
0

Niektóre z oferowanych rozwiązań wykorzystują ogólny argument E, aby przekazać typ wyjątku, który zostanie zgłoszony.

Przejdź o krok dalej i zamiast wprowadzać typ wyjątku, przekaż konsumentowi tego rodzaju wyjątek, jak w ...

Consumer<E extends Exception>

Możesz utworzyć kilka wariantów wielokrotnego użytku, Consumer<Exception>które pokryłyby typowe potrzeby aplikacji w zakresie obsługi wyjątków.

Rodney P. Barbati
źródło
0

Zrobię coś ogólnego:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

stosowanie:

 Lambda.handle(() -> method());
ahll
źródło
0

Użyj Jool Librarylub powiedz jOOλ libraryzJOOQ . Zapewnia nie tylko niesprawdzone interfejsy obsługiwane w wyjątkach, ale także zapewnia klasie Seq wiele przydatnych metod.

Zawiera także interfejsy funkcjonalne z maksymalnie 16 parametrami. Zapewnia także klasę Tuple, która jest używana w różnych scenariuszach.

Jool Git Link

W szczególności w poszukiwaniu biblioteki dla org.jooq.lambda.fi.util.functionpakietu. Zawiera wszystkie interfejsy z Java-8 z zaznaczonym sprawdzeniem. Zobacz poniżej w celach informacyjnych: -

wprowadź opis zdjęcia tutaj

Vinay Prajapati
źródło
0

Jestem autorem niewielkiej biblioteki lib z pewną ogólną magią, aby wrzucić dowolne wyjątki Java w dowolne miejsce bez potrzeby ich łapania lub pakowaniaRuntimeException .

Stosowanie: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

źródło: https://github.com/qoomon/unchecked-exceptions-java

qoomon
źródło
-7
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();
    }
}
Franky Knuckels
źródło
3
Chcesz komentować swoją pracę? Odpowiedzi zawierające tylko kod nie są tak przydatne.
Phantômaxx
@Franky, możesz naprawić prezentację, używając 4 odstępów zamiast <code>/<code>:)
AdrieanKhisbe