Dies SayHello
ist eine Single Abstract Method-Schnittstelle mit einer Methode, die eine Zeichenfolge verwendet und void zurückgibt. Dies ist analog zu einem Verbraucher. Sie stellen lediglich eine Implementierung dieser Methode in Form eines Verbrauchers bereit, die der folgenden anonymen Implementierung der inneren Klasse ähnelt.
SayHello sh = new SayHello() {
@Override
public void speak(String message) {
System.out.println("Hello " + message);
}
};
names.forEach(n -> sh.speak(n));
Glücklicherweise verfügt Ihre Schnittstelle über eine einzige Methode, sodass das Typsystem (formeller der Typauflösungsalgorithmus) auf seinen Typ als schließen kann SayHello
. Aber wenn es zwei oder mehr Methoden geben würde, wäre dies nicht möglich.
Ein viel besserer Ansatz besteht jedoch darin, den Verbraucher vor der for-Schleife zu deklarieren und wie unten gezeigt zu verwenden. Das Deklarieren der Implementierung für jede Iteration erzeugt mehr Objekte als nötig und erscheint mir kontraintuitiv. Hier ist die erweiterte Version, die Methodenreferenzen anstelle von Lambda verwendet. Die hier verwendete beschränkte Methodenreferenz ruft die relevante Methode für die hello
oben deklarierte Instanz auf.
SayHello hello = message -> System.out.println("Hello " + message);
names.forEach(hello::speak);
Aktualisieren
Angesichts der Tatsache, dass für staatenlose Lambdas, die nur einmal eine Instanz aus ihrem lexikalischen Geltungsbereich erfassen, niemals eine Instanz erstellt wird, erstellen beide Ansätze lediglich eine Instanz von SayHello
, und es gibt keinen Gewinn, der dem vorgeschlagenen Ansatz folgt. Dies scheint jedoch ein Implementierungsdetail zu sein, und ich wusste es bis jetzt nicht. Ein viel besserer Ansatz ist es also, einen Verbraucher an Ihren forEach weiterzugeben, wie im Kommentar unten vorgeschlagen. Beachten Sie auch, dass alle diese Ansätze nur eine Instanz Ihrer SayHello
Benutzeroberfläche erstellen, während die letzte prägnanter ist. So sieht es aus.
names.forEach(message -> System.out.println("Hello " + message));
Diese Antwort gibt Ihnen mehr Einblick. Hier ist der relevante Abschnitt aus dem JLS §15.27.4 : Laufzeitauswertung von Lambda-Ausdrücken
Diese Regeln sollen Implementierungen der Java-Programmiersprache Flexibilität bieten, indem:
- Ein neues Objekt muss nicht bei jeder Bewertung zugewiesen werden.
Tatsächlich dachte ich anfangs, dass jede Bewertung eine neue Instanz erstellt, was falsch ist. @ Holger danke für den Hinweis, guter Fang.
Consumer<String>
anstelle dersayHello
Schnittstelle verwenden.SayHello hello = message -> System.out.println("Hello " + message);
, es wird nur eineSayHello
Instanz geben. Angesichts der Tatsache, dass auch dieses eine Objekt hier ohnehin veraltet ist, da dasselbe über erreichbar istnames.forEach(n -> System.out.println("Hello " + n))
, gibt es keine Verbesserung in Ihrer „erweiterten Version“.Die Signatur von foreach lautet wie folgt:
Es nimmt ein Objekt des Verbrauchers auf. Die Consumer-Schnittstelle ist eine funktionale Schnittstelle (eine Schnittstelle mit einer einzelnen abstrakten Methode). Es akzeptiert eine Eingabe und gibt kein Ergebnis zurück.
Hier ist die Definition:
Daher kann jede Implementierung, die in Ihrem Fall, auf zwei Arten geschrieben werden:
ODER
Ebenso kann der Code für die SayHello-Klasse wie folgt geschrieben werden
ODER
names.foreach
Ruft also intern zuerst die Methode consumer.accept auf und führt ihre Lambda / anonyme Implementierung aus, die die SayHello-Lambda-Implementierung erstellt und aufruft, und so weiter. Mit dem Lambda-Ausdruck ist der Code sehr kompakt und klar. Sie müssen nur verstehen, wie es funktioniert, dh in Ihrem Fall wurde die Consumer-Oberfläche verwendet.Also endgültiger Ablauf:
foreach -> consumer.accept -> sayhello.speak
quelle