Ir al post : Supplier post = () -> “Post 1”
En el Post 1 vimos qué es la programación funcional y los conceptos clave. Hoy vamos a destripar el motor que la hace posible en Java: las expresiones lambda y los method references. Además, ya empezaremos a darle forma a nuestra Tienda Funcional.
1. Expresiones Lambda: Qué son y cómo se usan
Una expresión lambda es una forma concisa y anónima de representar una función. Es una característica clave introducida en Java 8 que permite escribir código con un estilo más funcional.
En términos sencillos, una lambda es un bloque de código que se puede pasar como argumento a un método. Te permite escribir una función sin necesidad de declararla como parte de una clase o de darle un nombre.
La sintaxis básica de una expresión lambda es:
(parámetros) -> cuerpo_de_la_expresion
parámetros
: Una lista de parámetros separados por comas, opcionalmente encerrados entre paréntesis. Los tipos de datos de los parámetros a menudo pueden ser inferidos por el compilador.->
: El operador flecha, que separa la lista de parámetros del cuerpo de la expresión. Se puede leer como “va a” o “se mapea a”.cuerpo_de_la_expresion
: El código que se ejecutará. Puede ser una sola expresión o un bloque de código encerrado entre llaves{}
.
Una expresión lambda se usa principalmente para proporcionar una implementación para el único método abstracto de una interfaz funcional.
Una interfaz funcional es una interfaz que contiene exactamente un método abstracto. Java incluye varias interfaces funcionales incorporadas en el paquete java.util.function
, como Predicate
, Consumer
, Function
, y Supplier
, además de otras ya existentes como Runnable
y Comparator
.
Ejemplos:
Predicate<Integer> esPar = n -> n % 2 == 0;
System.out.println(esPar.test(4)); // true
Function<String, Integer> largo = s -> s.length();
System.out.println(largo.apply("Tienda")); // 6
Consumer<String> imprimir = s -> System.out.println("Producto: " + s);
imprimir.accept("Laptop"); // Producto: Laptop
3. Method References
Una referencia a método (Method Reference) es una forma de sintaxis abreviada para una expresión lambda que simplemente invoca un método existente.
Introducidas en Java 8, las referencias a métodos hacen que el código sea más legible y conciso al permitirte referirte a un método por su nombre, en lugar de escribir la expresión lambda completa.
La idea fundamental es reemplazar una expresión lambda que simplemente reenvía sus argumentos a un método, como:
// Una lambda que llama a un método existente
(argumentos) -> NombreDeClase.metodo(argumentos)
con la sintaxis de referencia a método, mucho más limpia:
NombreDeClase::metodo
El operador clave es el doble dos puntos (::
), que separa el nombre de la clase o del objeto del nombre del método. Existen cuatro tipos principales de referencias a métodos:
1. Referencia a un Metodo Estatico: Se refiere a un método static
de una clase. La sintaxis es NombreDeClase::metodoEstatico
.
Ejemplo:
En lugar de (str) -> Integer.parseInt(str), se puede hacer:
Function<String, Integer> convertidor = Integer::parseInt;
Integer numero = convertidor.apply("123"); // numero = 123
2. Referencia a un Método de Instancia de un Objeto Particular: Se refiere a un método de una instancia de objeto específica. La sintaxis es objeto::metodoDeInstancia
.
Ejemplo: Si tienes un objeto System.out
, puedes referenciar su método println
.
// En lugar de una lambda (s -> System.out.println(s))
List<String> nombres = Arrays.asList("Ana", "Pedro");
nombres.forEach(System.out::println);
// Imprime:
// Ana
// Pedro
3. Referencia a un Método de Instancia de un Objeto Arbitrario de un Tipo Particular: Este tipo es el más común y a menudo el más confuso. El método se invoca en el primer argumento de la expresión lambda. La sintaxis es NombreDeClase::metodoDeInstancia
.
La expresión lambda (argumento) -> argumento.metodoDeInstancia()
se convierte en NombreDeClase::metodoDeInstancia
.
Ejemplo: Para obtener la longitud de cada String
en un Stream
:
List<String> palabras = Arrays.asList("hola", "mundo");
palabras.stream()
.map(String::length) // Referencia al método length() en cada objeto String
.forEach(System.out::println);
// Imprime:
// 4
// 5
4. Referencia a un Constructor
Se refiere a un constructor, que se trata como un método que devuelve una nueva instancia de la clase. La sintaxis es NombreDeClase::new
.
Ejemplo: Una lambda que crea un nuevo ArrayList
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
// En lugar de la lambda '() -> new ArrayList<>()'
Supplier<List<String>> proveedor = ArrayList::new;
List<String> lista = proveedor.get();
Ventajas de las Referencias a Métodos
- Legibilidad: El código se vuelve mucho más claro y fácil de entender. Por ejemplo,
String::length
es más descriptivo ques -> s.length()
. - Concisión: Reducen el código repetitivo, lo que hace que el código sea más corto y limpio.
- Claridad: Expresan directamente la intención de utilizar un método existente, lo cual mejora la comprensión del propósito del código.
El Arte de la Tienda Funcional: Cuando el Código Fluye como una Melodía
En el siguiente Post, construiremos un demo de una tienda funcional utilizando sintaxis de java 17+ e irnos olvidando de java 8.