Ich habe ein Problem beim Ausprobieren der Lambda-Ausdrücke von Java 8. Normalerweise funktioniert es gut, aber jetzt habe ich Methoden, die werfen IOException
. Am besten sehen Sie sich den folgenden Code an:
class Bank{
....
public Set<String> getActiveAccountNumbers() throws IOException {
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}
....
}
interface Account{
....
boolean isActive() throws IOException;
String getNumber() throws IOException;
....
}
Das Problem ist, dass es nicht kompiliert wird, da ich die möglichen Ausnahmen der isActive- und der getNumber-Methode abfangen muss. Aber selbst wenn ich explizit einen Try-Catch-Block wie unten verwende, wird er immer noch nicht kompiliert, da ich die Ausnahme nicht abfange. Entweder gibt es einen Fehler in JDK, oder ich weiß nicht, wie ich diese Ausnahmen abfangen soll.
class Bank{
....
//Doesn't compile either
public Set<String> getActiveAccountNumbers() throws IOException {
try{
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}catch(IOException ex){
}
}
....
}
Wie kann ich es zum Laufen bringen? Kann mich jemand auf die richtige Lösung hinweisen?
java
exception-handling
lambda
java-8
Martin Weber
quelle
quelle
Antworten:
Sie müssen die Ausnahme abfangen, bevor sie dem Lambda entgeht:
Bedenken Sie, dass das Lambda nicht an der Stelle ausgewertet wird, an der Sie es schreiben, sondern an einer völlig unabhängigen Stelle innerhalb einer JDK-Klasse. Dies wäre also der Punkt, an dem diese überprüfte Ausnahme ausgelöst würde, und an dieser Stelle wird sie nicht deklariert.
Sie können damit umgehen, indem Sie einen Wrapper Ihres Lambda verwenden, der geprüfte Ausnahmen in ungeprüfte übersetzt:
Ihr Beispiel würde geschrieben werden als
In meinen Projekten beschäftige ich mich mit diesem Problem, ohne es zu verpacken. Stattdessen verwende ich eine Methode, die die Überprüfung von Ausnahmen durch den Compiler effektiv entschärft. Es ist unnötig zu erwähnen, dass dies mit Vorsicht behandelt werden sollte und jeder im Projekt muss sich bewusst sein, dass eine geprüfte Ausnahme auftreten kann, wenn sie nicht deklariert ist. Dies ist der Installationscode:
und Sie können erwarten, einen
IOException
Wurf in Ihr Gesicht zu bekommen , obwohlcollect
es nicht deklariert. In den meisten, aber nicht allen realen Fällen möchten Sie die Ausnahme ohnehin nur erneut auslösen und als generischen Fehler behandeln. In all diesen Fällen geht nichts an Klarheit oder Richtigkeit verloren. Achten Sie nur auf die anderen Fälle, in denen Sie tatsächlich sofort auf die Ausnahme reagieren möchten. Der Entwickler wird vom Compiler nicht darauf aufmerksam gemacht, dass es dort einenIOException
zu fangenden gibt, und der Compiler wird sich tatsächlich beschweren, wenn Sie versuchen, ihn zu fangen, weil wir ihn getäuscht haben zu glauben, dass keine solche Ausnahme ausgelöst werden kann.quelle
Sie können Ihren statischen Schmerz auch mit Lambdas verbreiten, sodass das Ganze lesbar aussieht:
propagate
Hier wirdjava.util.concurrent.Callable
als Parameter empfangen und jede während des Aufrufs abgefangene Ausnahme in konvertiertRuntimeException
. Es gibt eine ähnliche Konvertierungsmethode Throwables # propagate (Throwable) in Guava.Diese Methode scheint für die Verkettung von Lambda-Methoden unerlässlich zu sein, daher hoffe ich, dass sie eines Tages zu einer der populären Bibliotheken hinzugefügt wird, oder dass dieses Ausbreitungsverhalten standardmäßig ist.
quelle
Mit dieser
UtilException
Hilfsklasse können Sie alle überprüften Ausnahmen in Java-Streams wie folgt verwenden:Note
Class::forName
wirftClassNotFoundException
, was überprüft wird . Der Stream selbst löst ebenfalls ausClassNotFoundException
und NICHT eine nicht aktivierte Ausnahme beim Umschließen.Viele andere Beispiele zur Verwendung (nach dem statischen Import
UtilException
):Verwenden Sie es jedoch nicht, bevor Sie die folgenden Vor- und Nachteile sowie Einschränkungen verstanden haben :
• Wenn der aufrufende Code die aktivierte Ausnahme behandeln soll, MÜSSEN Sie ihn der throw-Klausel der Methode hinzufügen, die den Stream enthält. Der Compiler zwingt Sie nicht mehr, es hinzuzufügen, daher ist es einfacher, es zu vergessen.
• Wenn der aufrufende Code die aktivierte Ausnahme bereits verarbeitet, erinnert Sie der Compiler daran, die throw-Klausel zur Methodendeklaration hinzuzufügen, die den Stream enthält (wenn Sie dies nicht tun, wird Folgendes angegeben: Die Ausnahme wird niemals im Hauptteil der entsprechenden try-Anweisung ausgelöst ).
• In jedem Fall können Sie den Stream selbst nicht umgeben, um die aktivierte Ausnahme INNERHALB der Methode, die den Stream enthält, abzufangen (wenn Sie es versuchen, sagt der Compiler: Ausnahme wird niemals in den Text der entsprechenden try-Anweisung ausgelöst).
• Wenn Sie eine Methode aufrufen, die buchstäblich nie die von ihr deklarierte Ausnahme auslösen kann, sollten Sie die throw-Klausel nicht einschließen. Beispiel: new String (byteArr, "UTF-8") löst UnsupportedEncodingException aus, aber UTF-8 wird durch die Java-Spezifikation garantiert, dass es immer vorhanden ist. Hier ist die Wurferklärung 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 niemals der 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 der hinzu löst eine Klausel der Methode aus, die den Stream enthält. Die aktivierte Ausnahme verhält sich dann wie eine nicht aktivierte Ausnahme.
• Wenn Sie eine strikte Schnittstelle implementieren, in der Sie nicht die Möglichkeit haben, eine Throws-Deklaration hinzuzufügen, und dennoch das Auslösen einer Ausnahme völlig angemessen ist, führt das Umschließen einer Ausnahme, um das Privileg zu erhalten, sie auszulösen, zu einer Stapelverfolgung mit falschen Ausnahmen die keine Informationen darüber beitragen, was tatsächlich schief gelaufen ist. Ein gutes Beispiel ist Runnable.run (), das keine überprüften Ausnahmen auslöst. In diesem Fall können Sie entscheiden, die aktivierte Ausnahme nicht zur throw-Klausel der Methode hinzuzufügen, die den Stream enthält.
• Wenn Sie die aktivierte Ausnahme NICHT zur Throws-Klausel der Methode, die den Stream enthält, hinzufügen (oder vergessen, sie hinzuzufügen), sollten Sie sich in jedem Fall dieser beiden Konsequenzen bewusst sein, wenn Sie CHECKED-Ausnahmen auslösen:
1) Der aufrufende Code kann ihn nicht beim Namen abfangen (wenn Sie es versuchen, sagt der Compiler: Eine Ausnahme wird niemals in den Text der entsprechenden try-Anweisung geworfen). Es sprudelt und wird wahrscheinlich in der Hauptprogrammschleife von einer "catch Exception" oder "catch Throwable" abgefangen, was Sie vielleicht sowieso wollen.
2) Es verstößt gegen das Prinzip der geringsten Überraschung: Es reicht nicht mehr aus, RuntimeException abzufangen, um das Abfangen aller möglichen Ausnahmen garantieren zu können. Aus diesem Grund glaube ich, dass dies nicht im Framework-Code erfolgen sollte, sondern nur im Business-Code, den Sie vollständig kontrollieren.
Fazit: Ich glaube, die Einschränkungen hier sind nicht ernst und die
UtilException
Klasse kann ohne Angst benutzt werden. Es liegt jedoch an Ihnen!quelle
Sie können möglicherweise Ihre eigenen rollen
Stream
Variante indem Sie Ihr Lambda umbrechen, um eine ungeprüfte Ausnahme auszulösen, und diese ungeprüfte Ausnahme später bei Terminaloperationen auspacken:Dann könnten Sie dies genauso verwenden wie a
Stream
:Diese Lösung würde einiges an Boilerplate erfordern, daher schlage ich vor, dass Sie sich die Bibliothek ansehen, die ich bereits erstellt habe und die genau das tut, was ich hier für die gesamte
Stream
Klasse beschrieben habe (und mehr!).quelle
new StreamBridge<>(ts, IOException.class);
->new StreamBridge<>(s, IOException.class);
StreamAdapter
.Verwenden Sie die Methode #propagate (). Beispiel für eine Nicht-Guava-Implementierung von Java 8-Blog von Sam Beran :
quelle
Dies beantwortet die Frage nicht direkt (es gibt viele andere Antworten, die dies tun), sondern versucht zunächst, das Problem zu vermeiden:
Nach meiner Erfahrung
Stream
ergibt sich die Notwendigkeit, Ausnahmen in einem (oder einem anderen Lambda-Ausdruck) zu behandeln, häufig aus der Tatsache, dass die Ausnahmen als von Methoden ausgelöst deklariert werden, bei denen sie nicht ausgelöst werden sollten. Dies ist häufig auf das Mischen von Geschäftslogik mit In- und Output zurückzuführen. IhreAccount
Benutzeroberfläche ist ein perfektes Beispiel:IOException
Betrachten Sie dieses Design, anstatt auf jeden Getter einen zu werfen :Die Methode
AccountReader.readAccount(…)
könnte ein Konto aus einer Datenbank oder einer Datei oder was auch immer lesen und eine Ausnahme auslösen, wenn dies nicht erfolgreich ist. Es wird einAccount
Objekt erstellt, das bereits alle Werte enthält und zur Verwendung bereit ist. Da die Werte bereits von geladen wurdenreadAccount(…)
, würden die Getter keine Ausnahme auslösen. So können Sie sie frei in Lambdas verwenden, ohne die Ausnahmen einwickeln, maskieren oder ausblenden zu müssen.Natürlich ist es nicht immer möglich, es so zu machen, wie ich es beschrieben habe, aber oft ist es das und es führt insgesamt zu saubererem Code (IMHO):
throws IOException
nicht ohne Verwendung überladen, sondern müssen den Compiler zufriedenstellenAccount
und von den Vorteilen profitieren (z. B. Gewindesicherheit).Account
in Lambdas zu verwenden (z. B. in aStream
).quelle
Es kann durch folgenden einfachen Code mit Stream und Try in AbacusUtil gelöst werden :
Offenlegung: Ich bin der Entwickler von
AbacusUtil
.quelle
Erweitern @marcg Lösung können Sie in der Regel eine werfen und fangen geprüft Ausnahme in Bächen; Das heißt, der Compiler wird Sie bitten, zu fangen / erneut zu werfen, so wie Sie sich außerhalb von Streams befanden !!
Dann würde Ihr Beispiel wie folgt geschrieben werden (Hinzufügen von Tests, um es klarer zu zeigen):
quelle
Um den IOException-Verarbeitungscode (zu RuntimeException) ordnungsgemäß hinzuzufügen, sieht Ihre Methode folgendermaßen aus:
Das Problem ist nun, dass das
IOException
als erfasstRuntimeException
und wieder in ein konvertiert werden mussIOException
- und dass dadurch der obigen Methode noch mehr Code hinzugefügt wird.Warum verwenden,
Stream
wenn es einfach so gemacht werden kann - und die Methode wirft,IOException
so dass auch dafür kein zusätzlicher Code benötigt wird:quelle
Unter Berücksichtigung dieses Problems habe ich eine kleine Bibliothek für den Umgang mit geprüften Ausnahmen und Lambdas entwickelt. Mit benutzerdefinierten Adaptern können Sie vorhandene Funktionstypen integrieren:
https://github.com/TouK/ThrowingFunction/
quelle
Ihr Beispiel kann wie folgt geschrieben werden:
Die Unthrow- Klasse kann hier https://github.com/SeregaLBN/StreamUnthrower genommen werden
quelle
Wenn es Ihnen nichts ausmacht, Bibliotheken von Drittanbietern zu verwenden, verfügt AOLs Cyclops-React Lib, Disclosure :: Ich bin ein Mitwirkender, über eine ExceptionSoftener- Klasse, die hier helfen kann.
quelle
Die Funktionsschnittstellen in Java deklarieren keine aktivierte oder deaktivierte Ausnahme. Wir müssen die Signatur der Methoden ändern von:
Zu:
Oder behandeln Sie es mit Try-Catch-Block:
Eine andere Möglichkeit besteht darin, einen benutzerdefinierten Wrapper zu schreiben oder eine Bibliothek wie ThrowingFunction zu verwenden. Mit der Bibliothek müssen wir nur die Abhängigkeit zu unserer pom.xml hinzufügen:
Und verwenden Sie die spezifischen Klassen wie ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.
Am Ende sieht der Code folgendermaßen aus:
quelle
Ich sehe keine Möglichkeit, geprüfte Ausnahmen im Stream (Java -8) zu behandeln. Die einzige Möglichkeit, die ich angewendet habe, besteht darin, geprüfte Ausnahmen im Stream abzufangen und sie als ungeprüfte Ausnahme erneut auszulösen.
quelle
Wenn Sie die Ausnahme innerhalb des Streams behandeln und die zusätzlichen weiter verarbeiten möchten, gibt es in DZone einen ausgezeichneten Artikel von Brian Vermeer, der das Konzept eines Entweder verwendet. Es zeigt eine hervorragende Möglichkeit, mit dieser Situation umzugehen. Das einzige, was fehlt, ist Beispielcode. Dies ist ein Beispiel meiner Erkundung unter Verwendung der Konzepte aus diesem Artikel.
quelle