Ir al post : Supplier post = () -> “Post 2”
En Java solemos pensar en objetos, clases y métodos… pero desde Java 8 el lenguaje nos dio otra forma de escribir código: programación funcional.
No es magia ni moda pasajera, es un paradigma que hace tu código más expresivo, fácil de mantener y hasta más rápido de leer.
Imperativo vs Funcional
- Imperativo → Tú dices cómo hacer algo, paso a paso.
- Funcional → Tú dices qué quieres obtener, y el lenguaje se encarga del “cómo”.
Ejemplo rápido:
Imperativo:
List<String> names = new ArrayList<>();
for (String name : people) {
if (name.startsWith("A")) {
names.add(name.toUpperCase());
}
}
System.out.println(names);
// Resultado: [ANA, ALBA]
Funcional (Java 8+):
List<String> names = people.stream()
.filter(name -> name.startsWith("A")) // Filtra ["Ana", "Alba"]
.map(String::toUpperCase) // Convierte a ["ANA", "ALBA"]
.toList();
System.out.println(names);
// Resultado: [ANA, ALBA]
💡 Menos código, más declarativo.
Por qué usarlo
- Menos código repetitivo → Adiós a los bucles infinitos.
- Más expresivo → El código dice qué haces, no cómo lo haces.
- Más fácil de paralelizar → Streams permiten procesamiento concurrente con poco esfuerzo.
- Más fácil de testear → Las funciones puras no dependen de estado externo.
Conceptos clave que vamos a usar en la serie
- Lambdas (Expresiones Lambda)
Son funciones anónimas (sin nombre) que puedes asignar a variables o pasar como argumento.
En lugar de escribir una clase completa solo para implementar un método, usas una lambda más compacta.
Ejemplo clásico:
Antes (clase anónima):
Comparator<String> comp = new Comparator<>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};
System.out.println(comp.compare("Hola", "Mundo"));
// Resultado: 0
Ahora (lambda):
Comparator<String> comp = (a, b) -> a.length() - b.length();
System.out.println(comp.compare("Hola", "Java"));
// Resultado: 0 (ambas palabras tienen 4 caracteres)
Piensa en lambdas como “mini funciones” que puedes mover como si fueran datos.
2. Interfaces Funcionales
Son interfaces con un solo método abstracto, lo que las hace compatibles con lambdas.
Java ya trae muchas interfaces funcionales listas para usar:
- Predicate<T> → recibe un
T
y devuelveboolean
.
Predicate<String> empiezaConA = s -> s.startsWith("A");
System.out.println(empiezaConA.test("Ana"));
// Resultado: true
- Function<T, R> → recibe un
T
y devuelve unR
.
Function<String, Integer> largo = s -> s.length();
System.out.println(largo.apply("Hola"));
// Resultado: 4
- Consumer<T> → recibe un
T
y no devuelve nada.
Consumer<String> imprimir = s -> System.out.println("Hola " + s);
imprimir.accept("Pedro");
// Resultado: Hola Pedro
- Supplier<T> → no recibe nada, devuelve un
T
.
Supplier<Double> aleatorio = () -> Math.random();
System.out.println(aleatorio.get());
// Resultado: número aleatorio entre 0.0 y 1.0
- BiFunction<T,U,R> → recibe dos argumentos y devuelve un valor.
BiFunction<Integer, Integer, Integer> suma = (a,b) -> a + b;
System.out.println(suma.apply(5, 7));
// Resultado: 12
La anotación @FunctionalInterface
es opcional, pero sirve para que el compilador te avise si rompes la regla de un solo método abstracto.
3. Stream API
Es una forma de procesar colecciones de datos declarativamente, usando operaciones en cadena.
Un stream es como una cinta transportadora: cada operación transforma o filtra los datos que pasan por ella.
Ejemplo:
List<String> nombres = List.of("Ana", "Pedro", "Alba");
List<String> mayusConA = nombres.stream()
.filter(n -> n.startsWith("A")) // ["Ana", "Alba"]
.map(String::toUpperCase) // ["ANA", "ALBA"]
.toList();
System.out.println(mayusConA);
// Resultado: [ANA, ALBA]
4. Composición de Funciones
Ejemplo con Function
:
Function<Integer, Integer> porDos = x -> x * 2;
Function<Integer, Integer> masTres = x -> x + 3;
Function<Integer, Integer> combinado = porDos.andThen(masTres);
System.out.println(combinado.apply(5));
// Resultado: 13
Ejemplo con Predicate
:
Predicate<String> empiezaConA = s -> s.startsWith("A");
Predicate<String> largoMayor3 = s -> s.length() > 3;
Predicate<String> filtro = empiezaConA.and(largoMayor3);
System.out.println(filtro.test("Ana")); // Resultado: false
System.out.println(filtro.test("Alberto")); // Resultado: true
Ejemplo rápido: Filtrar y transformar datos
Antes:
List<Integer> pares = new ArrayList<>();
for (int num : numeros) {
if (num % 2 == 0) {
pares.add(num * 10);
}
}
System.out.println(pares);
// Resultado (si numeros = [1,2,3,4]): [20, 40]
Ahora:
List<Integer> pares = numeros.stream()
.filter(n -> n % 2 == 0) // [2, 4]
.map(n -> n * 10) // [20, 40]
.toList();
System.out.println(pares);
// Resultado (si numeros = [1,2,3,4]): [20, 40]
Conclusión
La programación funcional en Java no es un adorno, es una forma distinta de pensar y estructurar el código. Ya viste que con lambdas, interfaces funcionales, Stream API y composición de funciones, podemos escribir menos código, más claro, y con menos errores potenciales.
En este primer paso entendimos qué es el paradigma, cuándo usarlo, y vimos ejemplos que dejan claro cómo pasar de imperativo a funcional.
Ahora viene lo bueno: aprender a manejar el combustible que hace posible todo esto — Lambda Expressions y Method References — para que no solo escribas código más corto, sino más expresivo y modular.
Próximo paso: Post 2 – Lambda Expressions y Method References
En el siguiente capítulo vamos a:
- Desmenuzar la sintaxis de lambdas y sus variaciones.
- Entender cuándo usar
()
y cuándo no. - Aprender a referenciar métodos y constructores de forma directa.
- Combinar lambdas con interfaces funcionales estándar.
Todo con ejemplos reales y prácticos.
Mini aplicación de práctica 💻
Durante esta serie, iremos construyendo una mini aplicación llamada “Tienda Funcional” donde:
- Tendremos una lista de productos (nombre, precio, categoría).
- Filtraremos por criterios dinámicos usando
Predicate
. - Transformaremos datos con
Function
. - Mostraremos reportes rápidos con
Consumer
. - Generaremos datos de prueba con
Supplier
. - Usaremos Streams para ordenar, agrupar y resumir información.
Primer objetivo práctico (Post 1 + Post 2):
- Crear la clase
Producto
con sus atributos. - Generar una lista de productos de ejemplo.
- Filtrar y transformar esa lista usando lambdas y Stream API.
Esto nos servirá como hilo conductor para que cada nuevo concepto que aprendamos lo implementemos en algo tangible.