usar lambdas funciones expresiones explicacion ejercicios ejemplos como anonimas java lambda java-8 type-inference

lambdas - java 8 lambda explicacion



¿Por qué los consumidores aceptan lambdas con cuerpos de declaración pero no cuerpos de expresión? (3)

El siguiente código sorprendentemente se está compilando con éxito:

Consumer<String> p = ""::equals;

Esto también:

p = s -> "".equals(s);

Pero esto falla con el error boolean cannot be converted to void como se esperaba:

p = s -> true;

La modificación del segundo ejemplo con paréntesis también falla:

p = s -> ("".equals(s));

¿Es un error en el compilador de Java o hay una regla de inferencia de tipo que no conozco?


Creo que las otras respuestas complican la explicación al centrarse en lambdas, mientras que su comportamiento en este caso es similar al comportamiento de los métodos implementados manualmente. Esto compila:

new Consumer<String>() { @Override public void accept(final String s) { "".equals(s); } }

mientras que esto no:

new Consumer<String>() { @Override public void accept(final String s) { true; } }

porque "".equals(s) es una declaración, pero true no lo es. Una expresión lambda para una interfaz funcional que devuelve void requiere una declaración para que siga las mismas reglas que el cuerpo de un método.

Tenga en cuenta que, en general, los cuerpos lambda no siguen exactamente las mismas reglas que los cuerpos de método; en particular, si una lambda cuyo cuerpo es una expresión implementa un método que devuelve un valor, tiene un return implícito. Entonces, por ejemplo, x -> true sería una implementación válida de Function<Object, Boolean> , mientras que true; No es un cuerpo de método válido. Pero en este caso particular, las interfaces funcionales y los cuerpos de los métodos coinciden.


Primero, vale la pena ver qué es realmente un Consumer<String> . De la documentación :

Representa una operación que acepta un único argumento de entrada y no devuelve ningún resultado . A diferencia de la mayoría de las otras interfaces funcionales, se espera que Consumer opere a través de efectos secundarios.

Por lo tanto, es una función que acepta una cadena y no devuelve nada.

Consumer<String> p = ""::equals;

Compila con éxito porque equals puede tomar una cadena (y, de hecho, cualquier objeto). El resultado de igual es simplemente ignorado. *

p = s -> "".equals(s);

Esto es exactamente lo mismo, pero con una sintaxis diferente. El compilador sabe que no debe agregar un return implícito porque un Consumer no debe devolver un valor. Sin embargo, agregaría un return implícito si la lambda fuera una Function<String, Boolean> .

p = s -> true;

Esto toma una ( s ) cadena ( s ) pero como true es una expresión y no una declaración, el resultado no puede ignorarse de la misma manera. El compilador tiene que agregar un return implícito porque una expresión no puede existir por sí sola. Por lo tanto, esto tiene un retorno: un booleano. Por lo tanto, no es un Consumer . **

p = s -> ("".equals(s));

Nuevamente, esta es una expresión , no una declaración. Ignorando las lambdas por un momento, verá la línea System.out.println("Hello"); tampoco se compilará si lo envuelve entre paréntesis.

* De la especificación :

Si el cuerpo de una lambda es una expresión de enunciado (es decir, una expresión que se le permitiría estar sola como enunciado), es compatible con un tipo de función que produce vacío; cualquier resultado simplemente se descarta.

** De la especificación (gracias, Eugene ):

Una expresión lambda es congruente con un tipo de función [que produce vacío] si ... el cuerpo lambda es una expresión de enunciado ( §14.8 ) o un bloque compatible con vacío.


s -> "".equals(s)

y

s -> true

no confíe en los mismos descriptores de funciones.

s -> "".equals(s) puede referirse a String->void o String->boolean function descriptor.
s -> true refiere solo a String->boolean descriptor de función String->boolean .

Por qué ?

  • cuando escribe s -> "".equals(s) , el cuerpo de la lambda: "".equals(s) es una declaración que produce un valor .
    El compilador considera que la función puede devolver void o boolean .

Entonces escribiendo:

Function<String, Boolean> function = s -> "".equals(s); Consumer<String> consumer = s -> "".equals(s);

es válida.

Cuando asigna el cuerpo lambda a una variable declarada Consumer<String> , se utiliza el descriptor String->void .
Por supuesto, este código no tiene mucho sentido (verificas la igualdad y no usas el resultado) pero al compilador no le importa.
Es lo mismo cuando escribe una declaración: myObject.getMyProperty() donde getMyProperty() devuelve un valor boolean pero no almacena el resultado.

  • cuando escribe s -> true , el cuerpo de la lambda: true es una sola expresión .
    El compilador considera que la función devuelve necesariamente boolean .
    Por lo tanto, solo se puede usar el descriptor String->boolean .

Ahora, regrese a su código que no se compila.
Que estás tratando de hacer ?

Consumer<String> p = s -> true;

No se puede. Desea asignar a una variable que utiliza el descriptor de función Consumer<String> un cuerpo lambda con el descriptor de función String->void . ¡No coincide!