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

enlace

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

enlace

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

enlace

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

enlace

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;