Welche der folgenden Methoden ist in Java 8 besser geeignet?
Java 8:
joins.forEach(join -> mIrc.join(mSession, join));
Java 7:
for (String join : joins) {
mIrc.join(mSession, join);
}
Ich habe viele for-Schleifen, die mit Lambdas "vereinfacht" werden könnten, aber gibt es wirklich einen Vorteil, sie zu verwenden? Würde es ihre Leistung und Lesbarkeit verbessern?
BEARBEITEN
Ich werde diese Frage auch auf längere Methoden ausweiten. Ich weiß, dass Sie die übergeordnete Funktion eines Lambda nicht zurückgeben oder aufheben können, und dies sollte auch beim Vergleich berücksichtigt werden. Aber gibt es noch etwas zu beachten?
java
for-loop
java-8
java-stream
nebkat
quelle
quelle
joins.forEach((join) -> mIrc.join(mSession, join));
wirklich eine "Vereinfachung" vonfor (String join : joins) { mIrc.join(mSession, join); }
? Sie haben die Interpunktionszahl von 9 auf 12 erhöht, um den Typ von zu verbergenjoin
. Was Sie wirklich getan haben, ist, zwei Aussagen in eine Zeile zu setzen.Antworten:
Die bessere Praxis ist zu verwenden
for-each
. Neben der Verletzung des Keep It Simple, Stupid- Prinzips weist der New-FangledforEach()
mindestens die folgenden Mängel auf:Nicht endgültige Variablen können nicht verwendet werden . Code wie der folgende kann also nicht in ein forEach-Lambda umgewandelt werden:
Überprüfte Ausnahmen können nicht behandelt werden . Lambdas ist es eigentlich nicht verboten, geprüfte Ausnahmen auszulösen, aber gängige Funktionsschnittstellen wie
Consumer
deklarieren keine. Daher muss jeder Code, der geprüfte Ausnahmen auslöst, diese intry-catch
oder umschließenThrowables.propagate()
. Aber selbst wenn Sie das tun, ist nicht immer klar, was mit der ausgelösten Ausnahme passiert. Es könnte irgendwo in den Eingeweiden von verschluckt werdenforEach()
Begrenzte Flusskontrolle . A
return
in einem Lambda ist gleich acontinue
in a für jeden, aber es gibt kein Äquivalent zu abreak
. Es ist auch schwierig, Dinge wie Rückgabewerte, Kurzschluss oder gesetzte Flags zu tun (was die Dinge ein wenig gelindert hätte, wenn es nicht gegen die Regel der nicht endgültigen Variablen verstoßen hätte ). "Dies ist nicht nur eine Optimierung, sondern auch wichtig, wenn Sie bedenken, dass einige Sequenzen (wie das Lesen der Zeilen in einer Datei) Nebenwirkungen haben können oder Sie eine unendliche Sequenz haben können."Könnte parallel ausgeführt werden , was für alle schrecklich ist, außer für 0,1% Ihres Codes, der optimiert werden muss. Jeder parallele Code muss durchdacht werden (auch wenn er keine Sperren, flüchtigen Stoffe und andere besonders unangenehme Aspekte der herkömmlichen Multithread-Ausführung verwendet). Jeder Fehler wird schwer zu finden sein.
Könnte die Leistung beeinträchtigen , da die JIT nicht für jedes () + Lambda im gleichen Maße wie einfache Loops optimieren kann, insbesondere jetzt, wo Lambdas neu sind. Mit "Optimierung" meine ich nicht den Aufwand für das Aufrufen von Lambdas (der klein ist), sondern die ausgefeilte Analyse und Transformation, die der moderne JIT-Compiler beim Ausführen von Code durchführt.
Wenn Sie Parallelität benötigen, ist es wahrscheinlich viel schneller und nicht viel schwieriger, einen ExecutorService zu verwenden . Streams sind sowohl automatisch (lesen Sie: Sie wissen nicht viel über Ihr Problem) als auch verwenden eine spezielle Parallelisierungsstrategie (lesen Sie: für den allgemeinen Fall ineffizient) ( Fork-Join-rekursive Zerlegung ).
Das Debuggen wird aufgrund der verschachtelten Aufrufhierarchie und, Gott bewahre, der parallelen Ausführung verwirrender . Der Debugger hat möglicherweise Probleme beim Anzeigen von Variablen aus dem umgebenden Code, und Dinge wie Step-Through funktionieren möglicherweise nicht wie erwartet.
Streams sind im Allgemeinen schwieriger zu codieren, zu lesen und zu debuggen . Tatsächlich gilt dies für komplexe " fließende " APIs im Allgemeinen. Die Kombination aus komplexen Einzelanweisungen, dem starken Einsatz von Generika und dem Fehlen von Zwischenvariablen führt zu verwirrenden Fehlermeldungen und einem frustrierten Debugging. Anstelle von "Diese Methode hat keine Überladung für Typ X" wird eine Fehlermeldung angezeigt, die näher an "Irgendwo, wo Sie die Typen durcheinander gebracht haben, aber wir wissen nicht, wo oder wie" liegt. Ebenso können Sie die Dinge in einem Debugger nicht so einfach durchgehen und untersuchen, wie wenn der Code in mehrere Anweisungen unterteilt ist und Zwischenwerte in Variablen gespeichert werden. Schließlich kann das Lesen des Codes und das Verstehen der Typen und des Verhaltens in jeder Ausführungsphase nicht trivial sein.
Sticht hervor wie ein schmerzender Daumen . Die Java-Sprache hat bereits die for-each-Anweisung. Warum durch einen Funktionsaufruf ersetzen? Warum sollte man ermutigen, Nebenwirkungen irgendwo in Ausdrücken zu verstecken? Warum unhandliche Einzeiler ermutigen? Regelmäßiges Mischen für jedes und neues für jedes wohl oder übel ist ein schlechter Stil. Code sollte in Redewendungen sprechen (Muster, die aufgrund ihrer Wiederholung schnell zu verstehen sind). Je weniger Redewendungen verwendet werden, desto klarer ist der Code und desto weniger Zeit wird für die Entscheidung verwendet, welche Redewendung verwendet werden soll (ein großer Zeitaufwand für Perfektionisten wie mich! ).
Wie Sie sehen, bin ich kein großer Fan von forEach (), außer in Fällen, in denen es Sinn macht.
Besonders anstößig für mich ist die Tatsache, dass
Stream
es nicht implementiert wirdIterable
(obwohl es tatsächlich eine Methode gibtiterator
) und nicht in einem for-each verwendet werden kann, nur mit einem forEach (). Ich empfehle, Streams mit in Iterables zu verwandeln(Iterable<T>)stream::iterator
. Eine bessere Alternative ist die Verwendung von StreamEx, mit dem eine Reihe von Stream-API-Problemen behoben werden, einschließlich der ImplementierungIterable
.Das heißt,
forEach()
ist nützlich für die folgenden:Atomare Iteration über eine synchronisierte Liste . Zuvor war eine mit generierte Liste in
Collections.synchronizedList()
Bezug auf Dinge wie get oder set atomar, beim Iterieren jedoch nicht threadsicher.Parallele Ausführung (unter Verwendung eines geeigneten parallelen Streams) . Dies erspart Ihnen ein paar Codezeilen im Vergleich zur Verwendung eines ExecutorService, wenn Ihr Problem mit den in Streams und Spliterators integrierten Leistungsannahmen übereinstimmt.
Bestimmte Container, die wie die synchronisierte Liste von der Kontrolle der Iteration profitieren (obwohl dies weitgehend theoretisch ist, es sei denn, die Benutzer können weitere Beispiele nennen).
Saubereres Aufrufen einer einzelnen Funktion mithilfe
forEach()
eines Methodenreferenzarguments (dhlist.forEach (obj::someMethod)
). Beachten Sie jedoch die Punkte zu aktivierten Ausnahmen, zum schwierigeren Debuggen und zur Reduzierung der Anzahl der Redewendungen, die Sie beim Schreiben von Code verwenden.Artikel, die ich als Referenz verwendet habe:
BEARBEITEN: Es sieht so aus, als ob einige der ursprünglichen Vorschläge für Lambdas (wie http://www.javac.info/closures-v06a.html ) einige der von mir erwähnten Probleme gelöst haben (natürlich unter Hinzufügung eigener Komplikationen).
quelle
forEach
soll den funktionalen Stil fördern, dh Ausdrücke ohne Nebenwirkungen verwenden. Wenn Sie auf eine Situation stoßen, dieforEach
mit Ihren Nebenwirkungen nicht gut funktioniert, sollten Sie das Gefühl haben, dass Sie nicht das richtige Werkzeug für den Job verwenden. Dann lautet die einfache Antwort: Das liegt daran, dass Ihr Gefühl richtig ist. Bleiben Sie also bei jeder Schleife. Die klassischefor
Schleife wurde nicht veraltet ...forEach
ohne Nebenwirkungen verwendet werden?forEach
ist die einzige Stream-Operation, die für Nebenwirkungen vorgesehen ist, aber nicht für Nebenwirkungen wie Ihren Beispielcode. Das Zählen ist eine typischereduce
Operation. Ich würde in der Regel vorschlagen, jede Operation, die lokale Variablen manipuliert oder den Kontrollfluss (einschließlich Ausnahmebehandlung) beeinflusst, in einer klassischenfor
Schleife beizubehalten. In Bezug auf die ursprüngliche Frage, denke ich, ergibt sich das Problem aus der Tatsache, dass jemand einen Stream verwendet, bei dem eine einfachefor
Schleife über die Quelle des Streams ausreichen würde. Verwenden Sie einen Stream, in demforEach()
nur funktioniertforEach
für die geeignet wäre?for
Schleifen.Der Vorteil kommt zum Tragen, wenn die Operationen parallel ausgeführt werden können. (Siehe http://java.dzone.com/articles/devoxx-2012-java-8-lambda-and - den Abschnitt über interne und externe Iteration.)
Der Hauptvorteil aus meiner Sicht ist, dass die Implementierung dessen, was innerhalb der Schleife zu tun ist, definiert werden kann, ohne entscheiden zu müssen, ob es parallel oder sequentiell ausgeführt wird
Wenn Sie möchten, dass Ihre Schleife parallel ausgeführt wird, können Sie einfach schreiben
Sie müssen zusätzlichen Code für die Thread-Behandlung usw. schreiben.
Hinweis: Für meine Antwort habe ich angenommen, dass Joins die
java.util.Stream
Schnittstelle implementieren . Wenn Joins nur diejava.util.Iterable
Schnittstelle implementiert, ist dies nicht mehr der Fall.quelle
map
& auftretenfold
, die nicht wirklich mit Lambdas zusammenhängen.Stream#forEach
undIterable#forEach
sind nicht dasselbe. OP fragt nachIterable#forEach
.joins
implementiertIterable
wirdStream
?joins.stream().forEach((join) -> mIrc.join(mSession, join));
joins.parallelStream().forEach((join) -> mIrc.join(mSession, join));
joins
Iterable
Wenn man diese Frage liest, kann man den Eindruck gewinnen, dass
Iterable#forEach
in Kombination mit Lambda-Ausdrücken eine Abkürzung / ein Ersatz für das Schreiben einer traditionellen for-each-Schleife ist. Das ist einfach nicht wahr. Dieser Code aus dem OP:ist nicht als Abkürzung zum Schreiben gedacht
und sollte auf keinen Fall auf diese Weise verwendet werden. Stattdessen ist es als Verknüpfung (obwohl es nicht genau dasselbe ist) zum Schreiben gedacht
Und es ist ein Ersatz für den folgenden Java 7-Code:
Das Ersetzen des Hauptteils einer Schleife durch eine Funktionsschnittstelle, wie in den obigen Beispielen, macht Ihren Code expliziter: Sie sagen, dass (1) der Hauptteil der Schleife den umgebenden Code und den Kontrollfluss nicht beeinflusst, und (2) der Der Hauptteil der Schleife kann durch eine andere Implementierung der Funktion ersetzt werden, ohne den umgebenden Code zu beeinflussen. Nicht auf nicht endgültige Variablen des äußeren Bereichs zugreifen zu können, ist kein Defizit an Funktionen / Lambdas, sondern ein Merkmal , das die Semantik
Iterable#forEach
von der Semantik einer herkömmlichen for-each-Schleife unterscheidet. Sobald man sich an die Syntax von gewöhnt hatIterable#forEach
, wird der Code besser lesbar, da Sie sofort diese zusätzlichen Informationen über den Code erhalten.Herkömmliche for-each-Schleifen bleiben in Java sicherlich eine gute Praxis (um den überstrapazierten Begriff " Best Practice " zu vermeiden ). Dies bedeutet jedoch nicht, dass
Iterable#forEach
dies als schlechte Praxis oder schlechter Stil angesehen werden sollte. Es ist immer eine gute Praxis, das richtige Werkzeug für die Arbeit zu verwenden, und dazu gehört auch das Mischen traditioneller for-each-SchleifenIterable#forEach
, wo dies sinnvoll ist.Da die Nachteile von
Iterable#forEach
bereits in diesem Thread besprochen wurden, sind hier einige Gründe, warum Sie wahrscheinlich verwenden möchtenIterable#forEach
:So machen Sie Ihren Code expliziter: Wie oben beschrieben,
Iterable#forEach
kann Ihr Code in einigen Situationen expliziter und lesbarer werden.So erweitern Sie Ihren Code erweiterbar und wartbar: Wenn Sie eine Funktion als Hauptteil einer Schleife verwenden, können Sie diese Funktion durch verschiedene Implementierungen ersetzen (siehe Strategiemuster ). Sie können beispielsweise den Lambda-Ausdruck leicht durch einen Methodenaufruf ersetzen, der von Unterklassen überschrieben werden kann:
Anschließend können Sie Standardstrategien mithilfe einer Aufzählung bereitstellen, die die Funktionsschnittstelle implementiert. Dies macht Ihren Code nicht nur erweiterbarer, sondern erhöht auch die Wartbarkeit, da die Schleifenimplementierung von der Schleifendeklaration entkoppelt wird.
So machen Sie Ihren Code debuggbarer: Das Trennen der Schleifenimplementierung von der Deklaration kann auch das Debuggen vereinfachen, da Sie möglicherweise über eine spezielle Debug-Implementierung verfügen, die Debug-Meldungen druckt, ohne dass Sie Ihren Hauptcode überladen müssen
if(DEBUG)System.out.println()
. Die Debug-Implementierung kann beispielsweise ein Delegat sein , der die eigentliche Funktionsimplementierung dekoriert .So optimieren Sie leistungskritischen Code: Entgegen einigen Aussagen in diesem Thread
Iterable#forEach
bietet er bereits eine bessere Leistung als eine herkömmliche Schleife für jede Schleife, zumindest wenn ArrayList verwendet und Hotspot im "-client" -Modus ausgeführt wird. Während diese Leistungssteigerung für die meisten Anwendungsfälle gering und vernachlässigbar ist, gibt es Situationen, in denen diese zusätzliche Leistung einen Unterschied machen kann. Zum Beispiel werden Bibliotheksverwalter sicherlich prüfen wollen, ob einige ihrer vorhandenen Schleifenimplementierungen durch ersetzt werden solltenIterable#forEach
.Um diese Aussage mit Fakten zu untermauern, habe ich mit Caliper einige Mikro-Benchmarks durchgeführt . Hier ist der Testcode (der neueste Caliper von git wird benötigt):
Und hier sind die Ergebnisse:
Bei Ausführung mit "-client" wird
Iterable#forEach
die herkömmliche for-Schleife über eine ArrayList übertroffen, sie ist jedoch immer noch langsamer als die direkte Iteration über ein Array. Bei Ausführung mit "-server" ist die Leistung aller Ansätze ungefähr gleich.Um optionale Unterstützung für die parallele Ausführung bereitzustellen: Es wurde hier bereits gesagt, dass die Möglichkeit, die funktionale Schnittstelle von
Iterable#forEach
parallel unter Verwendung von Streams auszuführen , sicherlich ein wichtiger Aspekt ist. DaCollection#parallelStream()
nicht garantiert wird, dass die Schleife tatsächlich parallel ausgeführt wird, muss dies als optionales Merkmal betrachtet werden. Wenn Sie Ihre Liste mit durchlaufenlist.parallelStream().forEach(...);
, sagen Sie explizit: Diese Schleife unterstützt die parallele Ausführung, hängt jedoch nicht davon ab. Auch dies ist ein Merkmal und kein Defizit!Indem Sie die Entscheidung für die parallele Ausführung von Ihrer eigentlichen Schleifenimplementierung entfernen, können Sie Ihren Code optional optimieren, ohne den Code selbst zu beeinflussen. Dies ist eine gute Sache. Wenn die Standardimplementierung für parallele Streams nicht Ihren Anforderungen entspricht, hindert Sie niemand daran, Ihre eigene Implementierung bereitzustellen. Sie können z. B. eine optimierte Sammlung bereitstellen, die vom zugrunde liegenden Betriebssystem, der Größe der Sammlung, der Anzahl der Kerne und einigen Voreinstellungen abhängt.
Das Schöne dabei ist, dass Ihre Loop-Implementierung diese Details nicht kennen oder sich nicht darum kümmern muss.
quelle
forEach
undfor-each
basierend auf einigen Kriterien bezüglich der Art des Schleifenkörpers zu wechseln . Weisheit und Disziplin, solche Regeln zu befolgen, sind das Markenzeichen eines guten Programmierers. Solche Regeln sind auch sein Fluch, weil die Menschen um ihn herum ihnen entweder nicht folgen oder nicht zustimmen. ZB mit aktivierten oder nicht aktivierten Ausnahmen. Diese Situation scheint noch nuancierter zu sein. Aber wenn der Körper "keinen Einfluss auf den Surround-Code oder die Flusskontrolle hat", wird er dann nicht besser als Funktion herausgerechnet?But, if the body "does not affect surround code or flow control," isn't factoring it out as a function better?
. Ja, dies wird meiner Meinung nach häufig der Fall sein - das Ausklammern dieser Schleifen als Funktionen ist eine natürliche Folge.Iterable#forEach
nur wegen der Leistungssteigerung Funktionsschleifen verwendet, die denen vor Java 8 ähneln . Das fragliche Projekt hat eine Hauptschleife ähnlich einer Spielschleife mit einer undefinierten Anzahl verschachtelter Unterschleifen, in die Clients Schleifenteilnehmer als Funktionen einstecken können. Eine solche Softwarestruktur profitiert stark davonIteable#forEach
.forEach()
kann so implementiert werden, dass sie schneller als für jede Schleife ist, da der Iterable den besten Weg kennt, seine Elemente zu iterieren, im Gegensatz zum Standard-Iterator. Der Unterschied ist also eine interne oder eine externe Schleife.Zum Beispiel
ArrayList.forEach(action)
kann einfach als implementiert werdenim Gegensatz zu der für jede Schleife, die viel Gerüst erfordert
Wir müssen jedoch auch zwei Gemeinkosten berücksichtigen, indem
forEach()
wir das Lambda-Objekt erstellen und das andere die Lambda-Methode aufrufen. Sie sind wahrscheinlich nicht signifikant.Siehe auch http://journal.stuffwithstuff.com/2013/01/13/iteration-inside-and-out/ zum Vergleichen interner / externer Iterationen für verschiedene Anwendungsfälle.
quelle
void forEach(Consumer<T> v){leftTree.forEach(v);v.accept(rootElem);rightTree.forEach(v);}
Dies ist eleganter als die externe Iteration, und Sie können entscheiden, wie Sie am besten synchronisierenString.join
Methoden (okay, falscher Join) "Anzahl der Elemente, die Arrays.stream-Overhead wahrscheinlich nicht wert sind." Also benutzen sie eine schicke for-Schleife.TL; DR :
List.stream().forEach()
war der schnellste.Ich hatte das Gefühl, ich sollte meine Ergebnisse aus der Benchmarking-Iteration hinzufügen. Ich habe einen sehr einfachen Ansatz gewählt (keine Benchmarking-Frameworks) und 5 verschiedene Methoden bewertet:
for
List.forEach()
List.stream().forEach()
List.parallelStream().forEach
das Testverfahren und die Parameter
Die Liste in dieser Klasse wird wiederholt und einige Mal
doIt(Integer i)
auf alle Mitglieder angewendet, jedes Mal über eine andere Methode. In der Hauptklasse führe ich die getestete Methode dreimal aus, um die JVM aufzuwärmen. Ich führe dann die Testmethode 1000 Mal aus und summiere die Zeit, die für jede Iterationsmethode benötigt wird (mitSystem.nanoTime()
). Nachdem das erledigt ist, dividiere ich diese Summe durch 1000 und das ist das Ergebnis, die durchschnittliche Zeit. Beispiel:Ich habe dies auf einer i5 4-Kern-CPU mit Java-Version 1.8.0_05 ausgeführt
klassisch
for
Ausführungszeit: 4,21 ms
klassischer foreach
Ausführungszeit: 5,95 ms
List.forEach()
Ausführungszeit: 3,11 ms
List.stream().forEach()
Ausführungszeit: 2,79 ms
List.parallelStream().forEach
Ausführungszeit: 3,6 ms
quelle
System.out.println
diese Daten nur naiv anzeigen, sind alle Ergebnisse nutzlos.System.nanoTime()
. Wenn Sie die Antwort lesen, werden Sie sehen, wie es gemacht wurde. Ich denke nicht, dass es nutzlos macht, da dies eine relative Frage ist. Es ist mir egal, wie gut eine bestimmte Methode war, es ist mir egal, wie gut sie im Vergleich zu den anderen Methoden war.Ich habe das Gefühl, dass ich meinen Kommentar etwas erweitern muss ...
Über Paradigma \ Stil
Das ist wahrscheinlich der bemerkenswerteste Aspekt. FP wurde populär, weil man Nebenwirkungen vermeiden kann. Ich werde nicht näher darauf eingehen, welche Vor- und Nachteile Sie daraus ziehen können, da dies nicht mit der Frage zusammenhängt.
Ich werde jedoch sagen, dass die Iteration mit Iterable.forEach von FP inspiriert ist und eher darauf zurückzuführen ist, dass mehr FP nach Java gebracht wird (ironischerweise würde ich sagen, dass forEach in reinem FP nicht viel Verwendung findet, da es nichts anderes als das Einführen tut Nebenwirkungen).
Am Ende würde ich sagen, dass es eher eine Frage des Geschmacks \ Stils \ Paradigmas ist, in dem Sie gerade schreiben.
Über Parallelität.
Aus Sicht der Leistung gibt es keine versprochenen nennenswerten Vorteile bei der Verwendung von Iterable.forEach gegenüber foreach (...).
Laut offiziellen Dokumenten auf Iterable.forEach :
... dh es ist ziemlich klar, dass es keine implizite Parallelität geben wird. Das Hinzufügen eines würde eine LSP-Verletzung darstellen.
Jetzt gibt es "Parallell-Sammlungen", die in Java 8 versprochen werden, aber um mit denen zu arbeiten, müssen Sie mich expliziter machen und besonders vorsichtig sein, um sie zu verwenden (siehe zum Beispiel die Antwort von mschenk74).
Übrigens: in diesem Fall Stream.forEach verwendet, und es wird nicht garantiert, dass die eigentliche Arbeit parallel ausgeführt wird (abhängig von der zugrunde liegenden Sammlung).
AKTUALISIEREN: Vielleicht nicht so offensichtlich und auf einen Blick etwas gedehnt, aber es gibt noch eine andere Facette von Stil und Lesbarkeit.
Zuallererst - einfache alte Forloops sind einfach und alt. Jeder kennt sie bereits.
Zweitens und noch wichtiger: Sie möchten Iterable.forEach wahrscheinlich nur mit einzeiligen Lambdas verwenden. Wenn "Körper" schwerer wird, sind sie in der Regel nicht so lesbar. Von hier aus haben Sie zwei Möglichkeiten: Verwenden Sie innere Klassen (yuck) oder verwenden Sie einen einfachen alten Forloop. Menschen ärgern sich oft, wenn sie sehen, dass dieselben Dinge (Iterationen über Sammlungen) auf verschiedene Arten / Stile in derselben Codebasis ausgeführt werden, und dies scheint der Fall zu sein.
Auch dies könnte ein Problem sein oder auch nicht. Kommt auf Leute an, die an Code arbeiten.
quelle
collection.forEach(MyClass::loopBody);
Eine der aufregendsten Funktionen
forEach
ist das Fehlen einer Unterstützung für geprüfte Ausnahmen.Eine mögliche Problemumgehung besteht darin, das Terminal
forEach
durch eine einfache alte foreach-Schleife zu ersetzen :Hier ist eine Liste der beliebtesten Fragen mit anderen Problemumgehungen zur Behandlung geprüfter Ausnahmen in Lambdas und Streams:
Java 8 Lambda-Funktion, die eine Ausnahme auslöst?
Java 8: Lambda-Streams, Filter nach Methode mit Ausnahme
Wie kann ich CHECKED-Ausnahmen aus Java 8-Streams heraus auslösen?
Java 8: Obligatorisch geprüfte Ausnahmebehandlung in Lambda-Ausdrücken. Warum obligatorisch, nicht optional?
quelle
Der Vorteil der Java 1.8 forEach-Methode gegenüber 1.7 Enhanced for loop besteht darin, dass Sie sich beim Schreiben von Code nur auf die Geschäftslogik konzentrieren können.
Die forEach-Methode verwendet also das Objekt java.util.function.Consumer als Argument Daher ist es hilfreich, unsere Geschäftslogik an einem separaten Speicherort zu haben, damit Sie sie jederzeit wiederverwenden können.
Schauen Sie sich das folgende Snippet an.
Hier habe ich eine neue Klasse erstellt, die die Methode "Akzeptanzklasse" aus der Verbraucherklasse überschreibt, in der Sie zusätzliche Funktionen hinzufügen können, "Mehr als Iteration". !!!!!!
quelle