Следующий код Java не компилируется:
@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
}
Компилятор сообщает:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
Странно то, что строка с пометкой «ОК» компилируется нормально, а строка с пометкой «Ошибка» не работает. Они кажутся практически идентичными.
{ }
изtakeBiConsumer
... и если да, то не могли бы вы привести пример ... если бы я прочитал это правильно,bc
является экземпляром класса / интерфейсаBiConsumer
, и , таким образом , должен содержать метод , называемыйaccept
в соответствии с интерфейсом подпись. .. ... и если это верно, тоaccept
метод должен быть где-то определен (например, класс, реализующий интерфейс) ... так вот что должно быть в{}
?? ... ... ... спасибо(String s1, String s2) -> "hi"
это экземпляр BiConsumer <String, String>.Ответы:
Ваша лямбда должна соответствовать
BiConsumer<String, String>
. Если вы обратитесь к JLS # 15.27.3 (Тип лямбда) :Таким образом, лямбда должна быть либо выражением оператора, либо блоком, совместимым с void:
источник
По сути,
new String("hi")
это исполняемый фрагмент кода, который действительно что-то делает (создает новую строку и затем возвращает ее). Возвращаемое значение можно игнорировать и по-new String("hi")
прежнему использовать в лямбда-выражении void-return для создания новой строки.Однако
"hi"
это просто константа, которая сама по себе ничего не делает. Единственное, что с ним можно сделать в теле лямбда - это вернуть его. Но лямбда-метод должен иметь возвращаемый типString
илиObject
, но он возвращаетvoid
, отсюда иString cannot be casted to void
ошибка.источник
String
литерал - это просто выражение, которое нельзя использовать в контексте оператора .()->x++
это законно, в то время как()->(x++)
, в основном, делать то же самое, не является…В первом случае все в порядке, потому что вы вызываете «специальный» метод (конструктор) и фактически не берете созданный объект. Чтобы было понятнее, я добавлю в лямбды необязательные фигурные скобки:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
И, что более ясно, я переведу это на более старые обозначения:
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" } });
В первом случае вы выполняете конструктор, но НЕ возвращаете созданный объект, во втором случае вы пытаетесь вернуть значение String, но ваш метод в вашем интерфейсе
BiConsumer
возвращает void, отсюда и ошибка компилятора.источник
JLS указывает, что
Теперь давайте посмотрим на это подробнее,
Поскольку ваш
takeBiConsumer
метод имеет тип void, получение лямбдаnew String("hi")
интерпретирует его как блок вроде{ new String("hi"); }
который действителен в пустоте, следовательно, первый случай компиляции.
Однако в случае, когда лямбда есть
-> "hi"
, такой блок, как{ "hi"; }
недопустимый синтаксис в java. Поэтому единственное, что можно сделать со словом "привет" - это попытаться вернуть его.
{ return "hi"; }
который недействителен в пустоте и объяснит сообщение об ошибке
incompatible types: bad return type in lambda expression java.lang.String cannot be converted to void
Для лучшего понимания обратите внимание, что если вы измените тип
takeBiConsumer
на String,-> "hi"
он будет действителен, поскольку он просто попытается напрямую вернуть строку.Обратите внимание, что сначала я подумал, что ошибка была вызвана тем, что лямбда находится в неправильном контексте вызова, поэтому я поделюсь этой возможностью с сообществом:
JLS 15.27
Однако в нашем случае мы находимся в контексте вызова, что правильно.
источник