usar tutorial expresiones explicacion español ejemplos como java lambda compiler-errors java-8 void

expresiones - java 8 tutorial español



¿Por qué este Java 8 lambda no se compila? (4)

El siguiente código Java no se compila:

@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 "OK" se compila bien, pero la línea marcada "Error" falla. Parecen esencialmente idénticos.


Básicamente, la new String("hi") es un código ejecutable que realmente hace algo (crea una nueva cadena y luego la devuelve). El valor devuelto se puede ignorar y la new String("hi") todavía se puede utilizar en lambda de retorno nulo 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 tendría que tener el tipo de retorno String u Object , pero devuelve void , por lo tanto, String cannot be casted to void error String cannot be casted to void .


El JLS especifica que

Si el resultado del tipo de función es nulo, el cuerpo lambda es una expresión de declaración (§14.8) o un bloque nulo compatible.

Ahora veamos eso en detalle,

Como su método takeBiConsumer es de tipo nulo, la lambda que recibe una new String("hi") lo interpretará como un bloque como

{ new String("hi"); }

que es válido en un vacío, de ahí la compilación del primer caso.

Sin embargo, en el caso donde el lambda es -> "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 explica 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 una cadena, -> "hi" será válido, ya que simplemente intentará devolver directamente la cadena.

Tenga en cuenta que al principio pensé que el error fue causado por el lambda al estar en un contexto de invocación incorrecto, por lo que compartiré esta posibilidad con la comunidad:

JLS 15.27

Es un error en tiempo de compilación si se produce una expresión lambda en un programa en un lugar que no sea un contexto de asignación (§5.2), un contexto de invocación (§5.3) o un contexto de conversión (§5.5).

Sin embargo, en nuestro caso, estamos en un contexto de invocación que es correcto.


El primer caso está bien porque está invocando un método "especial" (un constructor) y no está tomando el objeto creado. Solo 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.


Su lambda debe ser congruente con BiConsumer<String, String> . Si se refiere a JLS # 15.27.3 (Tipo de Lambda) :

Una expresión lambda es congruente con un tipo de función si se cumple todo lo siguiente:

  • [...]
  • Si el resultado del tipo de función es nulo, el cuerpo lambda es una expresión de declaración (§14.8) o un bloque nulo compatible.

Entonces, el lambda debe ser una expresión de declaración o un bloque vacío compatible: