Następujący kod Java nie może się skompilować:
@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
}
Kompilator raportuje:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
Dziwne jest to, że linia oznaczona jako „OK” kompiluje się dobrze, ale linia oznaczona jako „Error” zawodzi. Wydają się zasadniczo identyczne.
{ }
oftakeBiConsumer
... a jeśli tak, czy mógłbyś podać przykład ... jeśli przeczytałem to poprawnie,bc
jest to instancja klasy / interfejsuBiConsumer
, a zatem powinna zawierać metodę wywoływaną waccept
celu dopasowania sygnatury interfejsu. .. ... a jeśli to prawda, toaccept
metoda musi być gdzieś zdefiniowana (np. klasa implementująca interfejs) ... więc czy to powinno być w{}
?? ... ... ... dzięki(String s1, String s2) -> "hi"
jest to instancja BiConsumer <String, String>.Odpowiedzi:
Twoja lambda musi być zgodna z
BiConsumer<String, String>
. Jeśli odwołujesz się do JLS # 15.27.3 (typ Lambda) :Zatem lambda musi być wyrażeniem instrukcji lub blokiem zgodnym z void:
źródło
Zasadniczo
new String("hi")
jest to wykonywalny fragment kodu, który faktycznie coś robi (tworzy nowy ciąg, a następnie zwraca go). Zwracana wartość może zostać zignorowana inew String("hi")
nadal może być używana w lambdzie void-return do tworzenia nowego ciągu.Jednak
"hi"
jest to po prostu stała, która sama z siebie nic nie robi. Jedyną rozsądną rzeczą, jaką można z tym zrobić w ciele lambda, jest zwrócenie jej. Ale metoda lambda musiałaby mieć zwracany typString
lubObject
, ale zwracavoid
, stądString cannot be casted to void
błąd.źródło
String
literał jest po prostu wyrażeniem, którego nie można użyć w kontekście instrukcji .()->x++
Jest legalne, podczas gdy()->(x++)
zasadniczo robiąc dokładnie to samo, nie jest…Pierwszy przypadek jest w porządku, ponieważ wywołujesz „specjalną” metodę (konstruktora) i nie bierzesz w rzeczywistości utworzonego obiektu. Aby było to bardziej zrozumiałe, wstawię opcjonalne nawiasy klamrowe do twoich lambd:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
I dokładniej, przetłumaczę to na starszą notację:
takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) { public void accept(String s, String s2) { new String("hi"); // OK } }); takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) { public void accept(String s, String s2) { "hi"; // Here, the compiler will attempt to add a "return" // keyword before the "hi", but then it will fail // with "compiler error ... bla bla ... // java.lang.String cannot be converted to void" } });
W pierwszym przypadku wykonujesz konstruktor, ale NIE zwracasz utworzonego obiektu, w drugim przypadku próbujesz zwrócić wartość typu String, ale Twoja metoda w interfejsie
BiConsumer
zwraca void, stąd błąd kompilatora.źródło
JLS to określa
Zobaczmy teraz szczegółowo,
Ponieważ twoja
takeBiConsumer
metoda jest typu void, odbierająca lambdanew String("hi")
zinterpretuje ją jako blok{ new String("hi"); }
który jest ważny w void, stąd pierwszy przypadek kompilacji.
Jednak w przypadku, gdy lambda jest
-> "hi"
, blok taki jak{ "hi"; }
nie jest poprawną składnią w java. Dlatego jedyną rzeczą do zrobienia z „cześć” jest próba zwrócenia go.
{ return "hi"; }
który nie jest ważny w przypadku unieważnienia i wyjaśnij komunikat o błędzie
incompatible types: bad return type in lambda expression java.lang.String cannot be converted to void
Dla lepszego zrozumienia zwróć uwagę, że jeśli zmienisz typ
takeBiConsumer
na String,-> "hi"
będzie to poprawne, ponieważ po prostu spróbuje bezpośrednio zwrócić ciąg.Zauważ, że na początku myślałem, że błąd jest spowodowany tym, że lambda znajduje się w niewłaściwym kontekście wywołania, więc podzielę się tą możliwością ze społecznością:
JLS 15.27
Jednak w naszym przypadku znajdujemy się w kontekście wywołania, który jest poprawny.
źródło