usar una puede interfaces funciones funcionales expresiones ejercicios ejemplos como casos anonimas java lambda interface java-8 default-method

una - funciones anonimas java



¿Por qué no podemos usar métodos por defecto en expresiones lambda? (5)

Es más o menos una cuestión de alcance. De la JLS

A diferencia del código que aparece en las declaraciones de clase anónimas, el significado de los nombres y las palabras clave this y super aparecen en un cuerpo lambda, junto con la accesibilidad de las declaraciones referenciadas, son las mismas que en el contexto circundante (excepto que los parámetros lambda introducen nombres nuevos).

En tu intento de ejemplo

Formula formula = (a) -> sqrt( a * 100);

el alcance no contiene una declaración para el nombre sqrt .

Esto también se insinúa en el JLS

Hablando en términos prácticos, es inusual que una expresión lambda necesite hablar sobre sí misma (ya sea para llamarse recursivamente o para invocar sus otros métodos), mientras que es más común querer usar nombres para referirse a cosas en la clase adjunta que lo haría de lo contrario, se sombreará ( this , toString() ). Si es necesario que una expresión lambda se refiera a sí misma (como a través de this ), en su lugar se debe usar una referencia de método o una clase interna anónima.

Creo que podría haberse implementado. Eligieron no permitirlo.

Estaba leyendo este tutorial en Java 8 donde el escritor mostró el código:

interface Formula { double calculate(int a); default double sqrt(int a) { return Math.sqrt(a); } }

Y luego dijo

No se puede acceder a los métodos predeterminados desde expresiones lambda. El siguiente código no se compila:

Formula formula = (a) -> sqrt( a * 100);

Pero él no explicó por qué no es posible. Ejecuté el código, y me dio un error,

tipos incompatibles: Fórmula no es una interfaz funcional`

Entonces, ¿por qué no es posible o cuál es el significado del error? La interfaz cumple el requisito de una interfaz funcional que tiene un método abstracto.


Eso no es exactamente cierto. Los métodos predeterminados se pueden usar en expresiones lambda.

interface Value { int get(); default int getDouble() { return get() * 2; } } public static void main(String[] args) { List<Value> list = Arrays.asList( () -> 1, () -> 2 ); int maxDoubled = list.stream() .mapToInt(val -> val.getDouble()) .max() .orElse(0); System.out.println(maxDoubled); }

imprime 4 como se esperaba y usa un método predeterminado dentro de una expresión lambda ( .mapToInt(val -> val.getDouble()) )

Lo que el autor de tu artículo intenta hacer aquí

Formula formula = (a) -> sqrt( a * 100);

es definir una Formula , que funciona como interfaz funcional, directamente a través de una expresión lambda.

Eso funciona bien, en el código de ejemplo anterior, Value value = () -> 5 o con Formula como interfaz por ejemplo

Formula formula = (a) -> 2 * a * a + 1;

Pero

Formula formula = (a) -> sqrt( a * 100);

falla porque está intentando acceder al método sqrt ( this. ) pero no puede. Las lambdas, por especificación, heredan su alcance de su entorno, lo que significa que this dentro de una lambda se refiere a lo mismo que directamente fuera de ella. Y no hay un método sqrt afuera.

Mi explicación personal para esto: dentro de la expresión lambda, no está muy claro a qué interfaz funcional concreta se va a "convertir" la lambda. Comparar

interface NotRunnable { void notRun(); } private final Runnable r = () -> { System.out.println("Hello"); }; private final NotRunnable r2 = r::run;

La misma expresión lambda se puede "lanzar" a varios tipos. Lo pienso como si un lambda no tuviera un tipo. Es una función especial sin tipo que se puede usar para cualquier interfaz con los parámetros correctos. Pero esa restricción significa que no puede usar métodos del tipo futuro porque no puede conocerlo.


Esto agrega poco a la discusión, pero de todos modos me pareció interesante.

Otra forma de ver el problema sería pensarlo desde el punto de vista de una lambda autorreferencial.

Por ejemplo:

Formula formula = (a) -> formula.sqrt(a * 100);

Parecería que esto debería tener sentido, ya que para cuando se ejecuta la lambda, la referencia de la formula ya debe haberse inicializado (es decir, no hay forma de hacer formula.apply() hasta que la formula se haya inicializado correctamente, en cuyo caso, del cuerpo de la lambda, el cuerpo de apply , debería ser posible hacer referencia a la misma variable).

Sin embargo, esto tampoco funciona. Curiosamente, solía ser posible al principio. Puede ver que Maurice Naftalin lo había documentado en su sitio web de Lambda FAQ . Pero, por alguna razón, el soporte para esta función finalmente se eliminó .

Algunas de las sugerencias dadas en otras respuestas a esta pregunta ya han sido mencionadas allí en la misma discusión en la lista de correo de lambda.


Las expresiones Lambda funcionan de una manera completamente diferente a las clases anónimas en que this representa lo mismo que en el ámbito que rodea la expresión.

Por ejemplo, esto compila

class Main { public static void main(String[] args) { new Main().foo(); } void foo() { System.out.println(this); Runnable r = () -> { System.out.println(this); }; r.run(); } }

e imprime algo así como

Main@f6f4d33 Main@f6f4d33

En otras palabras, this es un Main , en lugar del objeto creado por la expresión lambda.

Por lo tanto, no puede usar sqrt en su expresión lambda porque el tipo de this referencia no es Formula , o un subtipo, y no tiene un método sqrt .

Formula es una interfaz funcional y el código

Formula f = a -> a;

compila y corre para mí sin ningún problema.

Aunque no puede usar una expresión lambda para esto, puede hacerlo utilizando una clase anónima, como esta:

Formula f = new Formula() { @Override public double calculate(int a) { return sqrt(a * 100); } };


Se puede acceder a los métodos predeterminados solo con referencias a objetos, si quieres acceder al método predeterminado, tendrás una referencia de objeto de la Interfaz funcional, en el cuerpo del método de expresión lambda no lo tendrás, así que no puedes acceder a él.

@FunctionalInterface un error incompatible types: Formula is not a functional interface porque no ha proporcionado la anotación @FunctionalInterface , si ha proporcionado obtendrá el error ''método indefinido'', el compilador lo obligará a crear un método en la clase.

@FunctionalInterface debe tener solo un método abstracto que su Interfaz tenga, pero le falta la anotación.

Pero los métodos estáticos no tienen tal restricción, ya que podemos acceder a ella sin referencia a los objetos como a continuación.

@FunctionalInterface public interface Formula { double calculate(int a); static double sqrt(int a) { return Math.sqrt(a); } } public class Lambda { public static void main(String[] args) { Formula formula = (a) -> Formula.sqrt(a); System.out.println(formula.calculate(100)); } }