Expresiones con lambda
Estructura normal de una expresión lambda: (parámetros) -> cuerpo de la expresión
Lista de argumentos | Operador | Cuerpo |
---|---|---|
(x,y) | -> | { x + y } |
Cuerpo de la expresión: Más de una sentencia requiere {} Parétesis para argumento más de uno.
Aspecto importante a tener en cuenta es que hay inferencia de tipos de datos, es decir en lo máximo posible no es necesario poner el tipo de dato. Esto implica expresiones más compactas y simples.
Debemos usar interfaces funcionales para usar expresiones lambda. Las más frecuentes entán en el paquete java.util.function.
Video explicativo de la sintáxis: enlace
Interfaces funcionales
Las interfaces funcionales son interfaces que tienen un método a implementar, es decir, un método abstracto. Esto significa que cada interfaz creada que respeta esta premisa se convierte automáticamente en una interfaz funcional. Toda expresión lambda debe convertirse o usar una interfaz funcional.
Interfaz Consumer
De las más sencillas recibe un argumento (genérico), pero no devuelve nada. Consumer representa una función con un argumento que no retorna nada. Tal y como su nombre indica, consume un valor y no genera nada. Un ejemplo de este tipo de funciones es el método de impresión por la salida estándar, que admite el texto a imprimir y no retorna nada. Ejemplo:
List<Integer> numbers = Arrays.asList(1,3,4,6);
numbers.forEach(number -> System.out.println(number));
El forEach puede recibir un Consumer como parámetro. Existe la opción Biconsumer, que tiene dos argumentos de entrada.
Interfaz Supplier
Supplier representa una función sin argumentos que retorna un resultado. Tal y como su nombre indica, genera un valor. Un ejemplo de este tipo de funciones es el método de generación de números aleatoriosde Math, que no requiere argumentos:
Supplier<Integer> randomNumbersSupp=() -> new Random().nextInt(10);
Supplier<LocalDateTime> s = () -> LocalDateTime.now();
LocalDateTime time = s.get();
System.out.println(time);
Interfaz Predicate
Recibe un argumento y devuelve un booleano. Sirve para comprobar si una condición es verdadera o falsa.
Predicate<String> checker = a -> a.startsWith("M");
System.out.println(checker.test("Miguel"));
Lo utilizaros para aspectos condicionales dentro de una expresión lambda. También existe la versión con dos argumentos Bipredicate.
Interfaz Function
Recibe un parámetro y devuelve otro parámetro. Hay versiones con más opciones (Bifunction por ejemplo).
package com.arquitecturajava.functional;
public class Principal {
@FunctionalInterface
interface Matematicas {
public double operacion(double x, double y);
}
public static void main(String[] args) {
Matematicas o = (x, y) -> x + y;
System.out.println(o.operacion(2, 3));
}
}
Similar a Function pero si queremos trabajar con siempre mismo tipo de datos podemos usar UnaryOperator. Por ejemplo:
UnaryOperator<Integer> multiplicarUnNumeroporDos = x -> x * 2;
Ejercicio 1
Crea una interfaz funcional, para usarla con lambda que devuelva el divisor más pequeño de un valor ( el parámetro de entrada).
Ejercicio 2
Crea una interfaz funcional, para usarla con lambda que recibe un parámetro tipo String y devuelve ese mismo String al revés. Entrada: Hola Mundo Salida: odnuM aloH
Ejercicio 3
Usa la interfaz supplier para “contener” un objeto de la clase:
public class Student {
private int id;
private String name;
private String gender;
private int age;