Der folgende Java-Code kann nicht kompiliert werden:
@FunctionalInterface
private interface BiConsumer<A, B> {
void accept(A a, B b);
}
private static void takeBiConsumer(BiConsumer<String, String> bc) { }
public static void main(String[] args) {
takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
takeBiConsumer((String s1, String s2) -> "hi"); // Error
}
Der Compiler meldet:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
Das Seltsame ist, dass die mit "OK" gekennzeichnete Zeile einwandfrei kompiliert wird, die mit "Fehler" gekennzeichnete Zeile jedoch fehlschlägt. Sie scheinen im Wesentlichen identisch zu sein.
{ }
vontakeBiConsumer
... und wenn ja, könnten Sie ein Beispiel geben ... wenn ich das richtig gelesen,bc
ist eine Instanz der Klasse / SchnittstelleBiConsumer
und somit ein Verfahren enthalten sollte , genanntaccept
die Schnittstelle Signatur übereinstimmen. .. ... und wenn das richtig ist, muss dieaccept
Methode irgendwo definiert werden (zB eine Klasse, die die Schnittstelle implementiert) ... also sollte das in der{}
?? ... ... ... danke(String s1, String s2) -> "hi"
handelt es sich um eine Instanz von BiConsumer <String, String>.Antworten:
Ihr Lambda muss kongruent sein mit
BiConsumer<String, String>
. Wenn Sie sich auf JLS # 15.27.3 (Typ eines Lambda) beziehen :Das Lambda muss also entweder ein Anweisungsausdruck oder ein void-kompatibler Block sein:
quelle
Grundsätzlich
new String("hi")
handelt es sich um einen ausführbaren Code, der tatsächlich etwas tut (er erstellt einen neuen String und gibt ihn dann zurück). Der zurückgegebene Wert kann ignoriert werden undnew String("hi")
kann weiterhin in void-return Lambda verwendet werden, um einen neuen String zu erstellen.Es
"hi"
ist jedoch nur eine Konstante, die nichts alleine macht. Das einzig Vernünftige im Lambda-Körper ist die Rückgabe . Aber die Lambda-Methode müsste den RückgabetypString
oder habenObject
, aber sie gibt zurückvoid
, daher derString cannot be casted to void
Fehler.quelle
String
Literal nur ein Ausdruck ist, der in einem Anweisungskontext nicht verwendet werden kann .()->x++
ist legal, während()->(x++)
im Grunde genau das gleiche nicht ist ...Der erste Fall ist in Ordnung, da Sie eine "spezielle" Methode (einen Konstruktor) aufrufen und das erstellte Objekt nicht tatsächlich übernehmen. Um es klarer zu machen, werde ich die optionalen Klammern in Ihre Lambdas stecken:
Und klarer, ich werde das in die ältere Notation übersetzen:
Im ersten Fall führen Sie einen Konstruktor aus, aber Sie geben das erstellte Objekt NICHT zurück. Im zweiten Fall versuchen Sie, einen String-Wert zurückzugeben, aber Ihre Methode in Ihrer Schnittstelle
BiConsumer
gibt void zurück, daher der Compilerfehler.quelle
Die JLS geben das an
Nun wollen wir das im Detail sehen,
Da Ihre
takeBiConsumer
Methode vom Typ void ist, wird sie vom Lambda-Empfangnew String("hi")
als blockartig interpretiertwas in einer Leere gültig ist, daher der erste Fall kompilieren.
In dem Fall, in dem sich das Lambda befindet
-> "hi"
, kann jedoch ein Block wie zist keine gültige Syntax in Java. Daher ist das einzige, was mit "hi" zu tun ist, zu versuchen, es zurückzugeben.
Dies ist nichtig und erklärt die Fehlermeldung
Beachten Sie zum besseren Verständnis, dass, wenn Sie den Typ eines Strings ändern
takeBiConsumer
,-> "hi"
dieser gültig ist, da einfach versucht wird, den String direkt zurückzugeben.Beachten Sie, dass ich zuerst dachte, der Fehler sei darauf zurückzuführen, dass sich das Lambda in einem falschen Aufrufkontext befindet. Daher teile ich diese Möglichkeit mit der Community:
JLS 15.27
In unserem Fall befinden wir uns jedoch in einem Aufrufkontext, der korrekt ist.
quelle