Introducción Expresiones Lambda

Introducción expresiones lambda

Las expresiones lambda fueron introducidas en Java 8 y proporcionan una forma más compacta y funcional de escribir código, especialmente cuando se trata de implementar interfaces funcionales (interfaces que tienen solo un método abstracto). Las expresiones lambda permiten pasar funciones como argumentos a métodos, lo que facilita el trabajo con funciones de orden superior y proporciona un estilo de programación más declarativo y conciso.

Concepto Descripción
Definición Función anónima que implementa interfaces funcionales (un solo método abstracto).
Sintaxis (parameters) -> expression
Ventajas - Código más conciso- Facilita la programación funcional- Mejora la legibilidad
Interfaces Funcionales - Function<T, R>: Entrada T, salida R
  • Predicate<T>: Entrada T, devuelve boolean
  • Consumer<T>: Entrada T, sin retorno
  • Supplier<T>: No toma entrada, devuelve T | | Ejemplos | - Function: (a) -> a + 1
  • Predicate: (n) -> n % 2 == 0
  • Consumer: (s) -> System.out.println(s)
  • Supplier: () -> Math.random() | | Uso en Colecciones | - forEach: list.forEach(item -> System.out.println(item));
  • Stream: list.stream().filter(s -> s.startsWith("J")).collect(Collectors.toList()); |

¿Qué es una expresión Lambda?

Una expresión lambda es una función anónima que se puede usar para implementar métodos de interfaces funcionales de manera más compacta. Se define mediante una sintaxis más simple que permite crear instancias de interfaces funcionales sin necesidad de crear clases o métodos adicionales.

Sintaxis básica de una expresión lambda:

(parameters) -> expression
  • parameters: Son los parámetros de entrada de la función, que pueden ser uno o más.
  • ->: Es el operador que separa los parámetros de la expresión lambda.
  • expression: Es la implementación del método de la interfaz funcional.

Ejemplo de una expresión Lambda:

Supongamos que tenemos una interfaz funcional que define un método para sumar dos números:

@FunctionalInterface
interface Sum {
    int add(int a, int b);
}

Una expresión lambda que implementa esta interfaz sería:

Sum sum = (a, b) -> a + b;
System.out.println(sum.add(5, 3));  // Imprime: 8

En este caso, la expresión lambda (a, b) -> a + b implementa el método add() de la interfaz Sum.


Ventajas de las Expresiones Lambda

  1. Concisión y claridad: Las expresiones lambda hacen que el código sea más compacto y fácil de leer, eliminando la necesidad de crear clases anónimas o métodos adicionales para interfaces funcionales.

    Ejemplo con clase anónima:

    Comparator<String> comparator = new Comparator<String>() {
        public int compare(String s1, String s2) {
            return s1.compareTo(s2);
        }
    };

    Ejemplo con lambda:

    Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2);
  2. Fomenta la programación funcional: Permite trabajar con funciones de manera más fluida, lo que facilita el uso de conceptos de programación funcional como las funciones de orden superior y el paso de funciones como argumentos.

  3. Mejora la legibilidad del código: Al reducir la cantidad de código necesario para realizar tareas como la creación de implementaciones de interfaces, las expresiones lambda permiten que el propósito de una función sea más claro.


Tipos de Interfaces Funcionales

Las expresiones lambda funcionan con interfaces funcionales. Estas son interfaces que tienen exactamente un método abstracto. En Java, estas interfaces están definidas en el paquete java.util.function.

Ejemplos de interfaces funcionales comunes:

  1. Function<T, R>: Representa una función que toma un argumento de tipo T y devuelve un valor de tipo R.

    Function<Integer, String> intToString = i -> "Número: " + i;
    System.out.println(intToString.apply(5));  // Imprime: "Número: 5"
  2. Predicate<T>: Representa una función que toma un argumento de tipo T y devuelve un valor booleano.

    Predicate<Integer> isEven = n -> n % 2 == 0;
    System.out.println(isEven.test(4));  // Imprime: true
  3. Consumer<T>: Representa una función que toma un argumento de tipo T y no devuelve ningún valor (acción de consumo).

    Consumer<String> print = s -> System.out.println(s);
    print.accept("Hola Lambda!");  // Imprime: "Hola Lambda!"
  4. Supplier<T>: Representa una función que no toma ningún argumento y devuelve un valor de tipo T.

    Supplier<Double> randomValue = () -> Math.random();
    System.out.println(randomValue.get());  // Imprime un número aleatorio

Uso de Expresiones Lambda con Colecciones

Las expresiones lambda son especialmente útiles cuando se trabaja con colecciones en Java, especialmente en operaciones como filtrado, mapeo y reducción. Java 8 introduce el uso de Streams para trabajar con colecciones de una manera funcional y declarativa.

Ejemplo con forEach() y Stream:

List<String> list = Arrays.asList("Java", "Python", "JavaScript");
list.forEach(item -> System.out.println(item));  // Imprime cada elemento de la lista

Ejemplo con filter() y map() en Streams:

List<String> list = Arrays.asList("Java", "Python", "JavaScript", "Ruby");

List<String> filteredList = list.stream()
    .filter(s -> s.startsWith("J"))  // Filtra los elementos que empiezan con 'J'
    .map(String::toUpperCase)         // Convierte los elementos a mayúsculas
    .collect(Collectors.toList());

System.out.println(filteredList);  // Imprime: [JAVA, JAVASCRIPT]

Sintaxis de Expresiones Lambda Complejas

Las expresiones lambda también pueden tener múltiples instrucciones, pero deben estar encerradas en un bloque de código.

Function<Integer, String> checkNumber = (n) -> {
    if (n % 2 == 0) {
        return "Par";
    } else {
        return "Impar";
    }
};
System.out.println(checkNumber.apply(5));  // Imprime: "Impar"