Java 8 Lambda-Funktion, die eine Ausnahme auslöst?


469

Ich weiß , wie eine Referenz auf ein Verfahren zu schaffen , das eine hat StringParameter und gibt ein int, das sind :

Function<String, Integer>

Dies funktioniert jedoch nicht, wenn die Funktion eine Ausnahme auslöst, beispielsweise wie folgt definiert:

Integer myMethod(String s) throws IOException

Wie würde ich diese Referenz definieren?





4
Die ganze Lösung sieht so aus, als wäre sie, abgesehen von Runtime-Ausnahmen, keine gute Lösung.
Also

5
Was ist mit Jool Library? cf org.jooq.lambda.Unchecked package
chaiyachaiya

Antworten:


402

Sie müssen einen der folgenden Schritte ausführen.

  • Wenn es Ihr Code ist, definieren Sie Ihre eigene Funktionsschnittstelle, die die aktivierte Ausnahme deklariert:

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

    und benutze es:

    void foo (CheckedFunction f) { ... }
  • Andernfalls schließen Sie Integer myMethod(String s)eine Methode ein, die keine aktivierte Ausnahme deklariert:

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

    und dann:

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

    oder:

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

7
Sie können tatsächlich erweitern Consumeroder Functionwenn Sie Standardmethoden verwenden - siehe meine Antwort unten.
jlb

2
Ich denke, dies kann als Einzeiler erreicht werden .
Ned Twigg

6
Kleinere Optimierung: Stattdessen kann auch (String t) -> myWrappedMethod(t)die Methodenreferenz this::myWrappedMethodverwendet werden.
Clashsoft

8
Eine noch allgemeinere Methode besteht darin, die aktivierte Funktion wie folgt zu definieren: @FunctionalInterface public interface CheckedFunction <T, R, E erweitert Ausnahme> {R apply (T t) löst E aus; } Auf diese Weise können Sie auch definieren, welche Ausnahme die Funktion auslöst, und die Schnittstelle für jeden Code wiederverwenden.
Martin Odhelius

3
Beeindruckend. Java ist schlimmer als ich dachte
user275801

194

Sie können tatsächlich Consumer(und Functionusw.) mit einer neuen Schnittstelle erweitern, die Ausnahmen behandelt - unter Verwendung der Standardmethoden von Java 8 !

Betrachten Sie diese Schnittstelle (erweitert 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;

}

Dann zum Beispiel, wenn Sie eine Liste haben:

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

Wenn Sie es (z. B. mit forEach) mit einem Code verwenden möchten , der Ausnahmen auslöst, hätten Sie traditionell einen try / catch-Block eingerichtet:

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

Mit dieser neuen Schnittstelle können Sie sie jedoch mit einem Lambda-Ausdruck instanziieren, und der Compiler wird sich nicht beschweren:

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

Oder wirf es einfach, um es prägnanter zu machen!:

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

Update : Anscheinend gibt es in Durian einen sehr schönen Teil der Utility-Bibliothek namens Errors, mit dem dieses Problem viel flexibler gelöst werden kann. Zum Beispiel habe ich in meiner obigen Implementierung die Fehlerbehandlungsrichtlinie ( System.out...oder explizit definiert)throw RuntimeException ) , während Sie mit Durians Fehlern eine Richtlinie im laufenden Betrieb über eine große Reihe von Dienstprogrammmethoden anwenden können. Vielen Dank für das Teilen , @NedTwigg!.

Beispielnutzung:

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

14
Sie haben also eine Reihe von Schnittstellen (Funktion, Verbraucher, Lieferant, ...) und eine Reihe von Richtlinien für die Behandlung von Fehlern (Throwing, System.out.println, ...). Ich denke, es gibt eine Möglichkeit , die Verwendung einer Richtlinie mit einer beliebigen Funktion zu vereinfachen, ohne kopieren und "ThrowingConsumer, ThrowingFunction usw." einfügen zu müssen.
Ned Twigg

1
Einige Zeit später ... Ich entschied mich dafür, ungeprüfte Ausnahmen zu verwenden und keine zusätzlichen Funktionsschnittstellen oder neuen Bibliotheken zu verwenden -> der einfache Weg, weniger Tippen, schnellere Zustellung, nicht wahr?
Aliopi

1
Hier ist eine verbesserte Version mit hinterhältiger Wurfsprache. RuntimeException muss nicht in CheckException entpackt werden.
Myui

61

Ich denke, Durians ErrorsKlasse kombiniert viele der Profis der verschiedenen obigen Vorschläge.

Um Durian in Ihr Projekt aufzunehmen, können Sie entweder:


Oder Sie können einfach RxJava verwenden, da Streams eine inhärente Fehlerbehandlung benötigen. Wenn sich in Ihrer Pipeline etwas befindet, das eine Ausnahme auslöst, besteht eine gute Chance, dass es sich wahrscheinlich um einen beobachtbaren Stream handelt. Dies erzwingt Java 8 auch nicht für nachgeschaltete Konsumenten einer Bibliothek.
Adam Gent

2
Bitte beachten Sie, dass Durian seit Juni 2016 keine neuen Versionen mehr hat. Kein Show-Stopper, aber etwas, das Sie beachten sollten.
Istvan Devai

9
Durian Betreuer hier. Was ist kaputt? Wenn ein Benutzer einen Fehler oder eine wichtige fehlende Funktion findet, wird ein Bugfix schnell veröffentlicht. Die Bibliothek ist einfach, daher hatten wir keine Fehlerberichte, daher mussten wir keine Bugfixes veröffentlichen.
Ned Twigg

28

Dies ist nicht spezifisch für Java 8. Sie versuchen, etwas zu kompilieren, das Folgendes entspricht:

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

15
Die Frage ist "Wie würde ich diese Referenz definieren?" . Dies beantwortet die Frage nicht wirklich; es verdeutlicht nur, wo das Problem liegt.
Dawood ibn Kareem

13

Haftungsausschluss: Ich habe Java 8 noch nicht verwendet, sondern nur darüber gelesen.

Function<String, Integer>wirft nicht IOException, also kannst du keinen Code hineinstecken throws IOException. Wenn Sie eine Methode aufrufen, die a erwartet Function<String, Integer>, kann das Lambda, das Sie an diese Methode übergeben, keinen IOExceptionPunkt auslösen . Sie können entweder ein Lambda wie dieses schreiben (ich denke, dies ist die Lambda-Syntax, nicht sicher):

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

Wenn Sie das Lambda an eine Methode übergeben, die Sie selbst geschrieben haben, können Sie eine neue Funktionsschnittstelle definieren und diese als Parametertyp verwenden, anstatt Function<String, Integer>:

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

Fügen Sie vor Ihrer Schnittstelle eine @ FunctionalInterface-Annotation hinzu. Nur dann kann sie für Lambdas verwendet werden.
Gangnus

13
@ Gangnus: Die @FunctionalInterfaceAnmerkung ist nicht erforderlich, damit sie für Lambdas verwendet werden kann. Es wird jedoch zur Überprüfung der geistigen Gesundheit empfohlen.
Tanmay Patil

9

Wenn es Ihnen nichts ausmacht, eine Drittanbieter- Bibliothek ( Vavr ) zu verwenden, können Sie schreiben

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

Es hat auch die sogenannte Try-Monade, die Fehler behandelt:

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

Bitte lesen Sie hier mehr .

Haftungsausschluss: Ich bin der Schöpfer von Vavr.


7

Sie können Unthrow Wrapper verwenden

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

oder

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

6

Sie können jedoch Ihr eigenes FunctionalInterface erstellen, das wie folgt ausgelöst wird.

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

Implementieren Sie es dann mit Lambdas oder Referenzen, wie unten gezeigt.

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

 }

}

6

Du kannst.

Erweitern von @marcg 's UtilExceptionund Hinzufügen von Generika, falls <E extends Exception>erforderlich: Auf diese Weise zwingt Sie der Compiler erneut, Throw-Klauseln hinzuzufügen, und alles ist so, als könnten Sie geprüfte Ausnahmen nativ in die Streams von Java 8 werfen.

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

5

Ich hatte dieses Problem mit Class.forName und Class.newInstance in einem Lambda, also habe ich einfach Folgendes getan:

public Object uncheckedNewInstanceForName (String name) {

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

Anstatt Class.forName ("myClass"). NewInstance () aufzurufen, habe ich im Lambda einfach uncheckedNewInstanceForName ("myClass") aufgerufen.


4

Eine andere Lösung, die einen Funktions-Wrapper verwendet, besteht darin, entweder eine Instanz eines Wrappers Ihres Ergebnisses zurückzugeben, z. B. Erfolg, wenn alles gut gelaufen ist, oder eine Instanz von beispielsweise Fehler.

Einige Codes zur Verdeutlichung:

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

Ein einfacher Anwendungsfall:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());

4

Dieses Problem hat mich auch gestört; Deshalb habe ich dieses Projekt erstellt .

Damit können Sie tun:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Es gibt insgesamt 39 vom JDK definierte Schnittstellen, die ein solches ThrowingÄquivalent haben. das ist alles @FunctionalInterfaces in Strömen (die Basis verwendet , Streamsondern auch IntStream, LongStreamundDoubleStream ).

Und da jeder von ihnen sein nicht werfendes Gegenstück erweitert, können Sie sie auch direkt in Lambdas verwenden:

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

Das Standardverhalten ist, dass, wenn Ihr Lambda eine aktivierte Ausnahme auslöst, a ThrownByLambdaExceptionmit der aktivierten Ausnahme als Ursache ausgelöst wird. Sie können das also erfassen und die Ursache ermitteln.

Weitere Funktionen sind ebenfalls verfügbar.


Ich mag die Idee wirklich, ich wünschte nur, Sie hätten die Throwables generisch gemacht, wie hier vorgeschlagen: javaspecialists.eu/archive/Issue221.html , zB: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- auf diese Weise muss der Benutzer nicht fangen Throwable, sondern stattdessen die speziell aktivierte Ausnahme.
Zoltán

@ Zoltán, das wäre allerdings ein Schmerz, die Ausnahme jedes Mal zu erklären; Außerdem können Sie immer einfach .apply () anstelle von .doApply () verwenden und fangen ThrownByLambdaException, Sie haben die ursprüngliche Ausnahme als Ursache (oder Sie können verwenden rethrow(...).as(MyRuntimeException.class))
fge

Ich denke, es gibt (irgendwie) einen Weg, dies zu umgehen .
Ned Twigg

@NedTwigg Ich habe das auch schon vor langer Zeit gelöst; Ich kann jetzt Throwing.runnable()und andere verwenden, immer mit Verkettungsfunktionen
14.

Die Verkettungsfunktion ist sehr cool! In meinem Kommentar ging es darum, ob ThrowingRunnable die generische Ausnahme haben sollte oder nicht. Zoltan fragte, ob Ihre Bibliothek das Argument als generischen Parameter haben könnte, und Sie sagten, das wäre ein Schmerz. Mein Link war zu einigen Codezeilen, die eine Möglichkeit zeigen, die Ausnahmen generisch zu gestalten, ohne dass dies schmerzhaft ist. Sofern ich es nicht falsch verstanden habe, sind die Ausnahmen in Ihrer Bibliothek nicht generisch (was eine vernünftige Designentscheidung ist, da Sie nicht viel Nutzen daraus ziehen, wenn Sie sie generisch machen).
Ned Twigg

4

Es gibt bereits viele großartige Antworten, die hier veröffentlicht wurden. Ich versuche nur, das Problem mit einer anderen Perspektive zu lösen. Es sind nur meine 2 Cent, bitte korrigieren Sie mich, wenn ich irgendwo falsch liege.

Die Throws-Klausel in FunctionalInterface ist keine gute Idee

Ich denke, dies ist aus folgenden Gründen wahrscheinlich keine gute Idee, um IOException-Würfe durchzusetzen

  • Das sieht für mich wie ein Anti-Pattern für Stream / Lambda aus. Die ganze Idee ist, dass der Anrufer entscheidet, welcher Code bereitgestellt werden soll und wie die Ausnahme behandelt werden soll. In vielen Szenarien ist die IOException möglicherweise nicht für den Client anwendbar. Zum Beispiel, wenn der Client einen Wert aus dem Cache / Speicher erhält, anstatt die eigentliche E / A auszuführen.

  • Außerdem wird die Behandlung von Ausnahmen in Streams wirklich abscheulich. Hier sieht mein Code beispielsweise so aus, als würde ich Ihre API verwenden

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

    Hässlich, nicht wahr? Wie ich in meinem ersten Punkt erwähnt habe, löst die doSomeOperation-Methode möglicherweise eine IOException aus oder nicht (abhängig von der Implementierung des Clients / Aufrufers), aber aufgrund der throw-Klausel in Ihrer FunctionalInterface-Methode muss ich immer die schreiben versuchen zu fangen.

Was mache ich, wenn ich wirklich weiß, dass diese API eine IOException auslöst?

  • Dann verwechseln wir FunctionalInterface wahrscheinlich mit typischen Interfaces. Wenn Sie wissen, dass diese API eine IOException auslöst, kennen Sie höchstwahrscheinlich auch ein standardmäßiges / abstraktes Verhalten. Ich denke, Sie sollten eine Schnittstelle definieren und Ihre Bibliothek (mit Standard- / abstrakter Implementierung) wie folgt bereitstellen

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

    Das Try-Catch-Problem besteht jedoch weiterhin für den Client. Wenn ich Ihre API im Stream verwende, muss ich die IOException immer noch in einem abscheulichen Try-Catch-Block behandeln.

  • Stellen Sie eine standardmäßige streamfreundliche API wie folgt bereit

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

    Die Standardmethode verwendet das Consumer-Objekt als Argument, das für die Behandlung der Ausnahme verantwortlich ist. Aus Sicht des Kunden sieht der Code nun so aus

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

    Schön, oder? Natürlich könnte anstelle von Exception :: printStackTrace ein Logger oder eine andere Verarbeitungslogik verwendet werden.

  • Sie können auch eine ähnliche Methode wie https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- verfügbar machen . Dies bedeutet, dass Sie eine andere Methode verfügbar machen können, die die Ausnahme vom vorherigen Methodenaufruf enthält. Der Nachteil ist, dass Sie Ihre APIs jetzt statusbehaftet machen, was bedeutet, dass Sie sich um die Thread-Sicherheit kümmern müssen und dass dies letztendlich zu einem Leistungseinbruch wird. Nur eine Option zu prüfen.


Ich bin damit einverstanden, dass das Konvertieren der aktivierten Ausnahme in eine nicht aktivierte Ausnahme oder das Verschlucken der Ausnahme keine gute Idee ist, da nicht bekannt ist, welches Element der StreamAusnahme ausgelöst hat. Daher gefällt mir die Idee, einen Ausnahmebehandler zu haben und die ungültigen Ergebnisse zu filtern. Beachten Sie, dass Ihre MyAmazingAPI effektiv eine ist FunctionalInterface(daher können Sie die Annotation @FunctionalInterface hinzufügen). Sie können auch einen Standardwert anstelle von verwenden Optional.empty().
Julien Kronegg

4

Sneaky Throw Idiom ermöglicht die Umgehung CheckedExceptiondes Lambda-Ausdrucks. Das Einwickeln von a CheckedExceptionin a RuntimeExceptionist nicht gut für eine strikte Fehlerbehandlung.

Es kann als ConsumerFunktion in einer Java-Sammlung verwendet werden.

Hier ist eine einfache und verbesserte Version von Jibs Antwort .

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

Dies hüllt das Lambda nur in einen Wurf . Es lässt CheckedExceptionalles zurückwerfen Exception, was in dein Lambda geworfen wurde.

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

}

Finden Sie eine vollständige Code und Unit - Tests hier .


3

Sie können hierfür ET verwenden . ET ist eine kleine Java 8-Bibliothek für die Konvertierung / Übersetzung von Ausnahmen.

Mit ET sieht es so aus:

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

ExceptionTranslatorInstanzen sind threadsicher und können von mehreren Komponenten gemeinsam genutzt werden. Sie können spezifischere Regeln für die Konvertierung von Ausnahmen konfigurieren (z. B. FooCheckedException -> BarRuntimeException), wenn Sie möchten. Wenn keine anderen Regeln verfügbar sind, werden aktivierte Ausnahmen automatisch in konvertiert RuntimeException.

(Haftungsausschluss: Ich bin der Autor von ET)


2
Sieht so aus, als wären Sie der Autor dieser Bibliothek. Laut Regeln SO , Sie müssen Ihre Zugehörigkeit in Ihren Antworten offen zu legen. Bitte fügen Sie Ihrer Antwort ausdrücklich hinzu, dass Sie diese Bibliothek geschrieben haben (dasselbe gilt für andere ET-bezogene Antworten).
Tagir Valeev

2
Hallo Tagir, danke für den Hinweis. Ich habe die Antwort aktualisiert.
Micha

2

Ich erlaube dem Benutzer, im Ausnahmefall den gewünschten Wert anzugeben. Ich habe also so etwas

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

Und das kann dann wie folgt aufgerufen werden:

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

1
Dies ist eine Erweiterung dieser Idee, bei der zwischen einer Strategie "Standardwert" (wie in Ihrer Antwort) und einer Strategie "RuntimeException neu definieren" unterschieden wird, bei der kein Standardwert erforderlich ist.
Ned Twigg

2

Wenn es Ihnen nichts ausmacht, eine Bibliothek eines Drittanbieters mit cyclops-react zu verwenden , einer Bibliothek, zu der ich beitrage, können Sie die FluentFunctions- API zum Schreiben verwenden

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

ofChecked nimmt eine jOOλ CheckedFunction und gibt die Referenz zurück zu einem Standard-JDK (nicht markiert) java.util.function.Function.

Alternativ können Sie über die FluentFunctions-API weiter mit der erfassten Funktion arbeiten!

Zum Beispiel, um Ihre Methode auszuführen, sie bis zu fünf Mal zu wiederholen und ihren Status zu protokollieren, den Sie schreiben können

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");

2

Standardmäßig erlaubt die Java 8- Funktion nicht, Ausnahmen auszulösen. Wie in mehreren Antworten vorgeschlagen, gibt es viele Möglichkeiten, dies zu erreichen. Eine Möglichkeit ist:

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

Definiere als:

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

Und fügen Sie throwsoder try/catchdie gleiche Ausnahme in der Aufrufermethode hinzu.


2

Erstellen Sie einen benutzerdefinierten Rückgabetyp, der die aktivierte Ausnahme weitergibt. Dies ist eine Alternative zum Erstellen einer neuen Schnittstelle, die die vorhandene Funktionsschnittstelle widerspiegelt, wobei die Methode der Funktionsschnittstelle geringfügig geändert wird.

Definition

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

}

Verwendungszweck

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

Was ist los?

Eine einzelne Funktionsschnittstelle, die eine aktivierte Ausnahme auslöst, wird erstellt ( CheckedValueSupplier). Dies ist die einzige funktionale Schnittstelle, die geprüfte Ausnahmen zulässt. Alle anderen Funktionsschnittstellen nutzen das CheckedValueSupplier, um Code zu verpacken, der eine aktivierte Ausnahme auslöst.

Die CheckedValueKlasse enthält das Ergebnis der Ausführung einer Logik, die eine aktivierte Ausnahme auslöst. Dies verhindert die Weitergabe einer aktivierten Ausnahme bis zu dem Punkt, an dem Code versucht, auf den Wert zuzugreifen, den eine Instanz CheckedValueenthält.

Die Probleme mit diesem Ansatz.

  • Wir werfen jetzt "Ausnahme" und verbergen effektiv den ursprünglich geworfenen spezifischen Typ.
  • Uns ist nicht bekannt, dass eine Ausnahme aufgetreten ist, bis sie CheckedValue#get()aufgerufen wird.

Consumer et al

Einige funktionale Schnittstellen ( Consumerzum Beispiel) müssen anders behandelt werden, da sie keinen Rückgabewert liefern.

Funktion anstelle des Verbrauchers

Ein Ansatz besteht darin, anstelle eines Verbrauchers eine Funktion zu verwenden, die bei der Verarbeitung von Streams angewendet wird.

    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

Eskalieren

Alternativ können Sie jederzeit zu a eskalieren RuntimeException. Es gibt andere Antworten, die die Eskalation einer aktivierten Ausnahme innerhalb von a abdecken Consumer.

Nicht konsumieren.

Vermeiden Sie einfach alle funktionalen Schnittstellen und verwenden Sie eine altmodische for-Schleife.


2

Ich verwende eine überladene Dienstprogrammfunktion namens, unchecked()die mehrere Anwendungsfälle behandelt.


EINIGE BEISPIELVERWENDUNGEN

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

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

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

UNTERSTÜTZUNG VON DIENSTLEISTUNGEN

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

0

Einige der angebotenen Lösungen verwenden ein generisches Argument von E, um den Typ der Ausnahme zu übergeben, die ausgelöst wird.

Gehen Sie noch einen Schritt weiter und übergeben Sie, anstatt den Typ der Ausnahme zu übergeben, einen Verbraucher des Ausnahmetyps, wie in ...

Consumer<E extends Exception>

Sie können mehrere wiederverwendbare Varianten erstellen, Consumer<Exception>die die allgemeinen Anforderungen für die Ausnahmebehandlung Ihrer Anwendung abdecken.


0

Ich werde etwas generisches tun:

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

        }
    }
}

Verwendungszweck:

 Lambda.handle(() -> method());

0

Verwenden Jool Libraryoder sagen jOOλ libraryvon JOOQ. Es bietet nicht nur ungeprüfte Schnittstellen mit Ausnahmebehandlung, sondern bietet der Seq-Klasse auch viele nützliche Methoden.

Außerdem enthält es funktionale Schnittstellen mit bis zu 16 Parametern. Außerdem bietet es eine Tupel-Klasse, die in verschiedenen Szenarien verwendet wird.

Jool Git Link

Speziell bei der Bibliothekssuche nach org.jooq.lambda.fi.util.functionPaket. Es enthält alle Schnittstellen von Java-8 mit aktiviertem Voranschlag. Siehe unten als Referenz: -

Geben Sie hier die Bildbeschreibung ein


0

Ich bin der Autor einer winzigen Bibliothek mit etwas generischer Magie, um jede Java-Ausnahme irgendwo hin zu werfen, ohne sie fangen oder einwickeln zu müssenRuntimeException .

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

Quelle: https://github.com/qoomon/unchecked-exceptions-java


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

3
Möchten Sie Ihre Arbeit kommentieren? Nur-Code-Antworten sind nicht so nützlich.
Phantômaxx

@Franky Sie können Ihre Präsentation mit 4 Abständen anstelle von <code>/<code>:)
korrigieren
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.