Zademy

Expresiones Lambda en Java: Guía Esencial para Recorridos y Fechas

Java; Lambdas; Stream; Fechas; Tutorial
941 palabras

Las expresiones lambda son una de las características más importantes introducidas en Java 8. Nos permiten escribir código más conciso y legible, especialmente al trabajar con colecciones y fechas. Esta guía te ayudará a comprender y utilizar las expresiones lambda más comunes en el desarrollo diario.

Qué son las Expresiones Lambda

Una expresión lambda es una función anónima que podemos pasar como argumento a métodos. Nos permite tratar la funcionalidad como si fuera un dato, haciendo nuestro código más flexible y expresivo.

Sintaxis Básica

La sintaxis de una lambda consta de tres partes:

(parámetros) -> { cuerpo de la función }

Ejemplo simple:

// Forma tradicional con clase anónima
Runnable r1 = new Runnable() {
    public void run() {
        System.out.println("Hola Mundo");
    }
};

// Forma con lambda (más concisa)
Runnable r2 = () -> System.out.println("Hola Mundo");

Recorrido de Colecciones con Lambdas

Recorrer Arrays

Los arrays se pueden recorrer de manera elegante utilizando lambdas y Streams:

String[] nombres = {"Ana", "Carlos", "Beatriz", "David"};

// Recorrido con forEach y lambda
Arrays.stream(nombres)
      .forEach(nombre -> System.out.println("Hola " + nombre));

// O con referencia a método (method reference)
Arrays.stream(nombres)
      .forEach(System.out::println);

Recorrer Listas

Las listas son quizás donde más utilizamos las expresiones lambda:

List<String> frutas = Arrays.asList("Manzana", "Banana", "Naranja", "Fresa");

// Recorrido básico
frutas.forEach(fruta -> System.out.println("Me gusta la " + fruta));

// Recorrido con números de posición
IntStream.range(0, frutas.size())
         .forEach(i -> System.out.println(i + ": " + frutas.get(i)));

// Filtrar y recorrer
frutas.stream()
      .filter(fruta -> fruta.length() > 6)
      .forEach(fruta -> System.out.println("Fruta larga: " + fruta));

Recorrer Mapas

Los mapas también se benefician enormemente de las lambdas:

Map<String, Integer> edades = new HashMap<>();
edades.put("Ana", 25);
edades.put("Carlos", 32);
edades.put("Beatriz", 28);

// Recorrer entry set (clave-valor)
edades.forEach((nombre, edad) ->
    System.out.println(nombre + " tiene " + edad + " años")
);

// Recorrer solo las claves
edades.keySet().forEach(clave ->
    System.out.println("Persona: " + clave)
);

// Recorrer solo los valores
edades.values().forEach(valor ->
    System.out.println("Edad: " + valor)
);

Manejo de Fechas con Lambdas

El API de fecha y hora introducido en Java 8 funciona perfectamente con lambdas.

Trabajar con LocalDate

LocalDate representa una fecha sin hora:

// Crear fechas
LocalDate hoy = LocalDate.now();
LocalDate navidad = LocalDate.of(2025, 12, 25);

// Operaciones con fechas usando streams
List<LocalDate> fechasImportantes = Arrays.asList(
    LocalDate.of(2025, 1, 1),
    LocalDate.of(2025, 5, 10),
    LocalDate.of(2025, 9, 16),
    LocalDate.of(2025, 12, 25)
);

// Filtrar fechas futuras
fechasImportantes.stream()
    .filter(fecha -> fecha.isAfter(hoy))
    .forEach(fecha -> System.out.println("Fecha futura: " + fecha));

// Formatear fechas
fechasImportantes.forEach(fecha ->
    System.out.println(fecha.format(DateTimeFormatter.ofPattern("dd 'de' MMMM 'de' yyyy")))
);

Trabajar con LocalTime

LocalTime representa una hora sin fecha:

// Crear horas
LocalTime ahora = LocalTime.now();
LocalTime almuerzo = LocalTime.of(14, 30);

// Lista de horas del día
List<LocalTime> horas = Arrays.asList(
    LocalTime.of(8, 0),   // Inicio de trabajo
    LocalTime.of(12, 0),  // Almuerzo
    LocalTime.of(14, 30),  // Regreso de almuerzo
    LocalTime.of(18, 0)   // Fin de trabajo
);

// Filtrar horas laborales
horas.stream()
    .filter(hora -> hora.isBefore(almuerzo))
    .forEach(hora -> System.out.println("Mañana: " + hora));

// Convertir a minutos del día
horas.forEach(hora ->
    System.out.println("Minutos del día: " + hora.toSecondOfDay() / 60)
);

Trabajar con LocalDateTime

LocalDateTime combina fecha y hora:

// Crear fechas con hora
LocalDateTime ahoraCompleto = LocalDateTime.now();
LocalDateTime reunion = LocalDateTime.of(2025, 11, 15, 10, 30);

// Lista de eventos
List<LocalDateTime> eventos = Arrays.asList(
    LocalDateTime.of(2025, 11, 8, 9, 0),   // Hoy, reunión matutina
    LocalDateTime.of(2025, 11, 15, 10, 30), // Próxima reunión
    LocalDateTime.of(2025, 12, 25, 0, 0)    // Navidad
);

// Filtrar eventos del mes actual
eventos.stream()
    .filter(evento -> evento.getMonth() == ahoraCompleto.getMonth())
    .forEach(evento -> System.out.println("Evento este mes: " + evento));

// Ordenar eventos por fecha
eventos.stream()
    .sorted()
    .forEach(evento -> System.out.println(
        "Evento: " + evento.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"))
    ));

Ejemplos Prácticos Combinados

Procesar Lista de Personas con Fechas

// Clase Persona
class Persona {
    private String nombre;
    private LocalDate fechaNacimiento;

    // constructor, getters...
}

List<Persona> personas = Arrays.asList(
    new Persona("Ana", LocalDate.of(1995, 6, 15)),
    new Persona("Carlos", LocalDate.of(1990, 12, 3)),
    new Persona("Beatriz", LocalDate.of(1998, 8, 20))
);

// Calcular edades y filtrar mayores de edad
LocalDate hoy = LocalDate.now();
personas.stream()
    .filter(p -> Period.between(p.getFechaNacimiento(), hoy).getYears() >= 18)
    .forEach(p -> {
        int edad = Period.between(p.getFechaNacimiento(), hoy).getYears();
        System.out.println(p.getNombre() + " tiene " + edad + " años");
    });

Agrupar Fechas por Mes

// Agrupar fechas por mes usando lambdas
List<LocalDate> fechas = Arrays.asList(
    LocalDate.of(2025, 1, 15),
    LocalDate.of(2025, 1, 20),
    LocalDate.of(2025, 2, 10),
    LocalDate.of(2025, 2, 25),
    LocalDate.of(2025, 3, 5)
);

Map<Month, List<LocalDate>> fechasPorMes = fechas.stream()
    .collect(Collectors.groupingBy(LocalDate::getMonth));

fechasPorMes.forEach((mes, listaFechas) -> {
    System.out.println("Mes: " + mes);
    listaFechas.forEach(fecha ->
        System.out.println("  - " + fecha.format(DateTimeFormatter.ofPattern("dd/MM")))
    );
});

Mejores Prácticas

Referencias a Método (Method References)

Cuando una lambda solo llama a un método existente, podemos usar referencias a método:

// En lugar de: nombre -> System.out.println(nombre)
nombres.forEach(System.out::println);

// En lugar de: fecha -> fecha.getMonth()
fechas.stream().map(LocalDate::getMonth).collect(Collectors.toList());

// En lugar de: (String s) -> s.length()
palabras.stream().mapToInt(String::length).sum();

Evitar Efectos Secundarios

Las lambdas deben ser puras cuando sea posible:

// ✅ Bien: Lambda pura
List<Integer> duplicados = numeros.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

// ❌ Evitar: Lambda con efectos secundarios
List<Integer> resultado = new ArrayList<>();
numeros.forEach(n -> {
    n = n * 2;
    resultado.add(n);  // Modificar estado externo
});

Manejo de Excepciones

Las lambdas no pueden lanzar excepciones verificadas directamente:

// ✅ Manejo con try-catch dentro
fechas.forEach(fecha -> {
    try {
        // Código que puede lanzar excepción
        LocalDate.parse(fecha.toString());
    } catch (DateTimeParseException e) {
        System.err.println("Fecha inválida: " + fecha);
    }
});

// ✅ Crear método auxiliar
private static void procesarFecha(LocalDate fecha) throws DateTimeParseException {
    // Código que puede lanzar excepción
}

fechas.forEach(LambdaFechasGUIA::procesarFecha);  // Envuelve la excepción

Conclusión

Las expresiones lambda han transformado la forma en que escribimos código Java, haciéndolo más conciso, legible y funcional. Dominar su uso con colecciones y fechas es esencial para cualquier desarrollador Java moderno.

Recuerda practicar estos conceptos gradualmente, comenzando con ejemplos simples y avanzando hacia casos más complejos. Las lambdas no solo hacen tu código más elegante, sino que también abren la puerta a paradigmas de programación funcional dentro de Java.


Esta guía se basa en la documentación oficial de Oracle y las mejores prácticas establecidas en la comunidad Java. Para profundizar, consulta los tutoriales oficiales de Oracle sobre Expresiones Lambda y Stream API.