El siguiente código Java no se puede 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
}
El compilador informa:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
Lo extraño es que la línea marcada como "OK" se compila bien, pero la línea marcada como "Error" falla. Parecen esencialmente idénticos.
{ }
detakeBiConsumer
... y si es así, ¿podría dar un ejemplo ... si leo esto correctamente,bc
es una instancia de la clase / interfazBiConsumer
y, por lo tanto, debería contener un método llamadoaccept
para que coincida con la firma de la interfaz? .. ... y si eso es correcto, entonces elaccept
método debe definirse en algún lugar (por ejemplo, una clase que implementa la interfaz) ... entonces, ¿qué debería estar en el{}
?? ... ... ... Gracias(String s1, String s2) -> "hi"
es una instancia de BiConsumer <String, String>.Respuestas:
Tu lambda debe ser congruente con
BiConsumer<String, String>
. Si se refiere a JLS # 15.27.3 (Tipo de Lambda) :Entonces, la lambda debe ser una expresión de declaración o un bloque compatible vacío:
fuente
Básicamente,
new String("hi")
es un fragmento de código ejecutable que realmente hace algo (crea una nueva cadena y luego la devuelve). El valor devuelto se puede ignorar ynew String("hi")
aún se puede usar en la lambda void-return para crear una nueva cadena.Sin embargo,
"hi"
es solo una constante que no hace nada por sí misma. Lo único razonable que se puede hacer con él en el cuerpo lambda es devolverlo . Pero el método lambda debería tener el tipo de retornoString
oObject
, pero devuelvevoid
, de ahí elString cannot be casted to void
error.fuente
String
literal es solo una expresión que no se puede usar en un contexto de declaración .()->x++
es legal, mientras que()->(x++)
, básicamente, hacer exactamente lo mismo, no es…El primer caso está bien porque está invocando un método "especial" (un constructor) y en realidad no está tomando el objeto creado. Para que quede más claro, pondré las llaves opcionales en sus lambdas:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
Y más claro, lo traduciré a la notación anterior:
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" } });
En el primer caso, está ejecutando un constructor, pero NO está devolviendo el objeto creado, en el segundo caso está intentando devolver un valor de cadena, pero su método en su interfaz
BiConsumer
devuelve vacío, de ahí el error del compilador.fuente
El JLS especifica que
Ahora veamos eso en detalle,
Dado que su
takeBiConsumer
método es de tipo vacío, la lambda que recibenew String("hi")
lo interpretará como un bloque como{ new String("hi"); }
que es válido en un vacío, de ahí el primer caso compilado.
Sin embargo, en el caso donde está la lambda
-> "hi"
, un bloque como{ "hi"; }
no es una sintaxis válida en java. Por lo tanto, lo único que se puede hacer con "hola" es intentar devolverlo.
{ return "hi"; }
que no es válido en un vacío y explique el mensaje de error
incompatible types: bad return type in lambda expression java.lang.String cannot be converted to void
Para una mejor comprensión, tenga en cuenta que si cambia el tipo de
takeBiConsumer
a String,-> "hi"
será válido ya que simplemente intentará devolver directamente la cadena.Tenga en cuenta que al principio pensé que el error se debió a que lambda estaba en un contexto de invocación incorrecto, por lo que compartiré esta posibilidad con la comunidad:
JLS 15.27
Sin embargo, en nuestro caso, estamos en un contexto de invocación que es correcto.
fuente