O seguinte código Java falha ao compilar:
@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
}
Os relatórios do compilador:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
O estranho é que a linha marcada "OK" compila bem, mas a linha marcada "Erro" falha. Eles parecem essencialmente idênticos.
{ }
detakeBiConsumer
... e se houver, você poderia dar um exemplo ... se eu li isso corretamente,bc
é uma instância da classe / interfaceBiConsumer
e, portanto, deve conter um método chamadoaccept
para corresponder à assinatura da interface. .. ... e se estiver certo, então oaccept
método precisa ser definido em algum lugar (por exemplo, uma classe que implementa a interface) ... então é isso que deve estar no{}
?? ... ... ... obrigado(String s1, String s2) -> "hi"
é uma instância de BiConsumer <String, String>.Respostas:
Seu lambda precisa ser congruente com
BiConsumer<String, String>
. Se você se referir a JLS # 15.27.3 (Tipo de Lambda) :Portanto, o lambda deve ser uma expressão de instrução ou um bloco compatível com void:
fonte
Basicamente,
new String("hi")
é um trecho de código executável que realmente faz algo (ele cria uma nova String e a retorna). O valor retornado pode ser ignorado enew String("hi")
ainda pode ser usado em lambda void-return para criar uma nova String.No entanto,
"hi"
é apenas uma constante que não faz nada por conta própria. A única coisa razoável a fazer com ele no corpo lambda é devolvê- lo. Mas o método lambda teria que ter um tipo de retornoString
ouObject
, mas ele retornavoid
, daí oString cannot be casted to void
erro.fonte
String
literal é apenas uma expressão que não pode ser usada em um contexto de instrução .()->x++
é legal, enquanto()->(x++)
, basicamente fazer exatamente o mesmo, não é ...O primeiro caso está ok porque você está invocando um método "especial" (um construtor) e não está realmente pegando o objeto criado. Só para deixar mais claro, colocarei as chaves opcionais em seus lambdas:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
E mais claro, vou traduzir isso para a notação mais antiga:
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" } });
No primeiro caso, você está executando um construtor, mas NÃO está retornando o objeto criado; no segundo caso, você está tentando retornar um valor String, mas seu método em sua interface
BiConsumer
retorna void, daí o erro do compilador.fonte
O JLS especifica que
Agora vamos ver isso em detalhes,
Uma vez que seu
takeBiConsumer
método é do tipo void, o receptor lambdanew String("hi")
irá interpretá-lo como um bloco como{ new String("hi"); }
que é válido em um vazio, daí a primeira compilação de caso.
No entanto, no caso em que o lambda é
-> "hi"
, um bloco como{ "hi"; }
não é uma sintaxe válida em java. Portanto, a única coisa a fazer com "oi" é tentar devolvê-lo.
{ return "hi"; }
que não é válido em um vazio e explica a mensagem de erro
incompatible types: bad return type in lambda expression java.lang.String cannot be converted to void
Para um melhor entendimento, note que se você alterar o tipo de
takeBiConsumer
para String,-> "hi"
será válido pois simplesmente tentará retornar diretamente a string.Observe que, a princípio, achei que o erro era causado pelo lambda estar em um contexto de invocação incorreto, portanto, compartilharei essa possibilidade com a comunidade:
JLS 15.27
No entanto, em nosso caso, estamos em um contexto de invocação correto.
fonte