Ich schätze die neuen Java 8-Funktionen für Lambdas und Standardmethoden-Schnittstellen sehr. Trotzdem langweilen mich geprüfte Ausnahmen. Wenn ich zum Beispiel nur alle sichtbaren Felder eines Objekts auflisten möchte, möchte ich einfach Folgendes schreiben:
Arrays.asList(p.getClass().getFields()).forEach(
f -> System.out.println(f.get(p))
);
Da die get
Methode jedoch möglicherweise eine aktivierte Ausnahme auslöst, die nicht mit dem Consumer
Schnittstellenvertrag übereinstimmt , muss ich diese Ausnahme abfangen und den folgenden Code schreiben:
Arrays.asList(p.getClass().getFields()).forEach(
f -> {
try {
System.out.println(f.get(p));
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
);
In den meisten Fällen möchte ich jedoch nur, dass die Ausnahme als geworfen wird RuntimeException
und das Programm die Ausnahme ohne Kompilierungsfehler verarbeiten kann oder nicht.
Ich würde gerne Ihre Meinung zu meiner umstrittenen Problemumgehung für gestörte Ausnahmen erfahren. Zu diesem Zweck habe ich eine Hilfsschnittstelle ConsumerCheckException<T>
und eine Utility-Funktion rethrow
( aktualisiert gemäß dem Vorschlag von Dovals Kommentar ) wie folgt erstellt:
@FunctionalInterface
public interface ConsumerCheckException<T>{
void accept(T elem) throws Exception;
}
public class Wrappers {
public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
return elem -> {
try {
c.accept(elem);
} catch (Exception ex) {
/**
* within sneakyThrow() we cast to the parameterized type T.
* In this case that type is RuntimeException.
* At runtime, however, the generic types have been erased, so
* that there is no T type anymore to cast to, so the cast
* disappears.
*/
Wrappers.<RuntimeException>sneakyThrow(ex);
}
};
}
/**
* Reinier Zwitserloot who, as far as I know, had the first mention of this
* technique in 2009 on the java posse mailing list.
* http://www.mail-archive.com/[email protected]/msg05984.html
*/
public static <T extends Throwable> T sneakyThrow(Throwable t) {
throw (T) t;
}
}
Und jetzt kann ich einfach schreiben:
Arrays.asList(p.getClass().getFields()).forEach(
rethrow(f -> System.out.println(f.get(p)))
);
Ich bin mir nicht sicher, ob dies die beste Redewendung ist, um die überprüften Ausnahmen umzukehren, aber wie ich erklärte, hätte ich gerne einen bequemeren Weg, um mein erstes Beispiel zu erreichen, ohne mit überprüften Ausnahmen umzugehen, und dies ist der einfachere Weg, den ich gefunden habe es zu tun.
quelle
sneakyThrow
inside ofrethrow
auslösen, anstatt sie in a zu verpackenRuntimeException
. Alternativ können Sie auch die@SneakyThrows
Annotation von Project Lombok verwenden , die dasselbe tut.Consumer
s inforEach
bei Verwendung von parallelenStream
s parallel ausgeführt werden kann . Ein Throwable, der vom Withing des Verbrauchers ausgelöst wurde, wird dann an den aufrufenden Thread weitergegeben, was 1) die anderen gleichzeitig laufenden Verbraucher nicht anhält, was angemessen sein kann oder nicht, und 2) wenn mehr als einer der Verbraucher nur etwas auslöst Eines der Threads wird vom aufrufenden Thread gesehen.Antworten:
Vor- und Nachteile sowie Einschränkungen Ihrer Technik:
Wenn der aufrufende Code die aktivierte Ausnahme behandeln soll, MUSS er der throws-Klausel der Methode hinzugefügt werden, die den Stream enthält. Der Compiler wird Sie nicht mehr zum Hinzufügen zwingen, sodass Sie es leichter vergessen können. Zum Beispiel:
Wenn der aufrufende Code die geprüfte Ausnahme bereits behandelt, werden Sie vom Compiler daran erinnert, die throws-Klausel zur Methodendeklaration hinzuzufügen, die den Stream enthält. .
In jedem Fall können Sie den Stream selbst nicht umgeben, um die aktivierte Ausnahme INNERHALB der Methode abzufangen, die den Stream enthält (wenn Sie es versuchen, sagt der Compiler: Ausnahme wird niemals im Hauptteil der entsprechenden try-Anweisung ausgelöst).
Wenn Sie eine Methode aufrufen, die die deklarierte Ausnahme buchstäblich nie auslösen kann, sollten Sie die throws-Klausel nicht einschließen. Beispiel: Ein neuer String (byteArr, "UTF-8") löst eine UnsupportedEncodingException aus, aber die Java-Spezifikation garantiert, dass UTF-8 immer vorhanden ist. Hier ist die Deklaration der Würfe ein Ärgernis und jede Lösung, um sie mit minimalem Boilerplate zum Schweigen zu bringen, ist willkommen.
Wenn Sie geprüfte Ausnahmen hassen und der Meinung sind, dass sie zunächst nie zur Java-Sprache hinzugefügt werden sollten (eine wachsende Anzahl von Menschen denkt so, und ich bin NICHT einer von ihnen), fügen Sie die geprüfte Ausnahme einfach nicht zu den Würfen hinzu Klausel der Methode, die den Stream enthält. Die aktivierte Ausnahme verhält sich dann wie eine nicht aktivierte Ausnahme.
Wenn Sie eine strenge Schnittstelle implementieren, in der Sie keine Option zum Hinzufügen einer Throws-Deklaration haben und dennoch eine Ausnahme auslösen können, führt das Umschließen einer Ausnahme, um das Privileg des Auslösens zu erhalten, zu einem Stacktrace mit unechten Ausnahmen, die keine Angaben dazu machen, was tatsächlich schief gelaufen ist. Ein gutes Beispiel ist Runnable.run (), das keine aktivierten Ausnahmen auslöst. In diesem Fall können Sie entscheiden, die aktivierte Ausnahme nicht der Throws-Klausel der Methode hinzuzufügen, die den Stream enthält.
Wenn Sie die geprüfte Ausnahme NICHT zur throws-Klausel der Methode hinzufügen (oder vergessen), die den Stream enthält, müssen Sie auf jeden Fall die folgenden zwei Konsequenzen berücksichtigen, wenn Sie CHECKED-Ausnahmen auslösen:
Der aufrufende Code kann ihn nicht anhand des Namens abfangen (wenn Sie es versuchen, sagt der Compiler: Ausnahme wird niemals in den Hauptteil der entsprechenden try-Anweisung geworfen). Es sprudelt und wird wahrscheinlich von einer "catch Exception" oder "catch Throwable" in der Hauptprogrammschleife abgefangen, was Sie vielleicht sowieso wollen.
Dies verstößt gegen das Prinzip der geringsten Überraschung: Es reicht nicht mehr aus, RuntimeException abzufangen, um sicherzustellen, dass alle möglichen Ausnahmen abgefangen werden. Aus diesem Grund sollte dies meines Erachtens nicht im Framework-Code erfolgen, sondern nur im Business-Code, den Sie vollständig steuern.
Verweise:
HINWEIS: Wenn Sie sich für diese Technik entscheiden, können Sie die
LambdaExceptionUtil
Hilfsklasse von StackOverflow kopieren : https://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8 -Flüsse . Sie erhalten die vollständige Implementierung (Funktion, Verbraucher, Lieferant ...) mit Beispielen.quelle
Kann es in diesem Beispiel jemals wirklich scheitern? Denken Sie nicht so, aber möglicherweise ist Ihr Fall speziell. Wenn es wirklich "nicht scheitern kann" und es nur eine nervige Compiler-Sache ist, möchte ich die Ausnahme einpacken und ein
Error
mit einem Kommentar "nicht passieren kann" auslösen. Macht die Dinge für die Wartung sehr klar . Andernfalls werden sie sich fragen: "Wie kann das passieren?" Und "Wer zum Teufel macht das?"Dies ist in einer kontroversen Praxis so YMMV. Ich werde wahrscheinlich ein paar Abstimmungen bekommen.
quelle
clone
einen versiegelten Typ an, vonFoo
dem bekannt ist, dass er ihn unterstützt, und er wirftCloneNotSupportedException
. Wie könnte das passieren, wenn der Code nicht mit einer anderen unerwarteten Art von Code verknüpft würdeFoo
? Und wenn das passiert, kann man irgendetwas vertrauen?An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
(zitiert aus dem Javadoc vonError
) Dies ist genau diese Art von Situation, die bei weitem nicht unzureichend ist. Das Werfen einesError
Here ist die am besten geeignete Option.Eine andere Version dieses Codes, bei der die aktivierte Ausnahme nur verzögert ist:
Die Magie ist, dass, wenn Sie anrufen:
dann ist Ihr Code verpflichtet, die Ausnahme zu fangen
quelle
sneakyThrow ist cool! Ich fühle total deinen Schmerz.
Wenn Sie in Java bleiben müssen ...
Paguro hat funktionale Schnittstellen, die geprüfte Ausnahmen umbrechen, so dass Sie nicht mehr darüber nachdenken müssen. Es enthält unveränderliche Sammlungen und Funktionstransformationen in Anlehnung an Clojure-Transducer (oder Java 8-Streams), die die funktionalen Schnittstellen berücksichtigen und geprüfte Ausnahmen umbrechen.
Es gibt auch VAVr und The Eclipse Collections zum Ausprobieren.
Verwenden Sie andernfalls Kotlin
Kotlin ist 2-Wege-kompatibel mit Java, hat keine geprüften Ausnahmen, keine Grundelemente (na ja, über die der Programmierer nicht nachdenken muss), ein besseres Typsystem, setzt Unveränderlichkeit voraus usw. Auch wenn ich die Paguro-Bibliothek oben kuratiere, habe ich Ich konvertiere den gesamten Java-Code, den ich kann, nach Kotlin. Alles, was ich früher in Java gemacht habe, mache ich jetzt lieber in Kotlin.
quelle
Überprüfte Ausnahmen sind sehr nützlich und ihre Handhabung hängt von der Art des Produkts ab, zu dem Ihr Code gehört:
Normalerweise darf eine Bibliothek keine geprüften Ausnahmen verarbeiten, sondern muss als von der öffentlichen API geworfen deklariert werden. Der seltene Fall, in dem sie in RuntimeExcepton eingebunden werden könnten, besteht darin, im Bibliothekscode ein privates XML-Dokument zu erstellen und es zu analysieren, eine temporäre Datei zu erstellen und diese dann zu lesen.
Für Desktop-Anwendungen müssen Sie die Produktentwicklung starten, indem Sie Ihr eigenes Framework für die Ausnahmebehandlung erstellen. Wenn Sie ein eigenes Framework verwenden, verpacken Sie normalerweise eine aktivierte Ausnahme in zwei bis vier Wrapper (Ihre benutzerdefinierten Unterklassen von RuntimeExcepion) und behandeln sie mit einem einzigen Exception-Handler.
Bei Servern wird Ihr Code normalerweise innerhalb eines Frameworks ausgeführt. Wenn Sie keinen (nackten) Server verwenden, erstellen Sie ein eigenes Framework (basierend auf den oben beschriebenen Laufzeiten).
Für akademische Übungen können Sie diese jederzeit direkt in RuntimeExcepton einbinden. Jemand kann auch einen winzigen Rahmen schaffen.
quelle
Vielleicht möchten Sie sich dieses Projekt ansehen . Ich habe es speziell wegen des Problems der überprüften Ausnahmen und funktionalen Schnittstellen erstellt.
Damit können Sie dies tun, anstatt Ihren benutzerdefinierten Code zu schreiben:
Da alle diese Throwing * -Schnittstellen ihre nicht-Throwing-Gegenstücke erweitern, können Sie sie direkt im Stream verwenden:
Oder Sie können einfach:
Und andere Dinge.
quelle
fields.forEach((ThrowingConsumer<Field>) f -> out.println(f.get(o)))