Translate

miércoles, 24 de diciembre de 2025

Programación funcional con Optional, Stream y CompletableFuture en Java

 


Aunque Java no hable directamente de mónadas, muchas de sus clases modernas (desde Java 8) implementan comportamiento monádico: permiten encadenar operaciones que transforman o combinan valores sin salir del contexto (por ejemplo: puede ser nulo, puede fallar, puede no haber terminado aún).


Una mónada encapsula un valor junto con un contexto.

Ejemplos de contextos:

  • “puede no haber valor” → Optional<T>
  • “flujo de valores” → Stream<T>
  • “resultado asíncrono” → CompletableFuture<T>


Una mónada define operaciones para:

  • aplicar funciones al valor (map, flatMap)
  • propagar la ausencia o el error automáticamente
  • encadenar transformaciones sin condicionales explícitos


Optional<T> — ausencia de valor


El caso más simple: representa un valor o nada.


Optional<Integer> x = Optional.of(5);


var y = x.map(n -> n * 2)

          .flatMap(n -> Optional.of(n + 3))

          .orElse(0); // (5 * 2) + 3 = 13



Métodos monádicos principales

  • map(f): Aplica f si el valor existe
  • flatMap(f): Aplica f que devuelve otro Optional
  • orElse(v): Valor por defecto si está vacío
  • orElseGet(supplier): Valor por defecto perezoso
  • or(fallback) (desde Java 9): Usa otro Optional si está vacío


Stream<T> — secuencias monádicas

Stream también es una mónada: un contexto que contiene una secuencia de valores y permite transformaciones encadenadas.


Stream.of(1, 2, 3)

    .map(x -> x * 2)

    .flatMap(x -> Stream.of(x, x + 10))

    .forEach(System.out::println);


  • map transforma cada elemento
  • flatMap expande (aplana) los resultados
  • filter, reduce, etc. siguen el mismo espíritu


En términos monádicos: Stream es la mónada de la lista.


CompletableFuture<T> — mónada asíncrona


CompletableFuture encapsula un valor que aún no está disponible.


CompletableFuture.supplyAsync(() -> 5)

    .thenApply(x -> x * 2)           // map

    .thenCompose(x -> asyncAdd(x))   // flatMap

    .exceptionally(e -> 0)           // orElse

    .thenAccept(System.out::println);


  • thenApply ≈ map
  • thenCompose ≈ flatMap
  • exceptionally ≈ orElse


Los métodos monádicos permiten escribir código sin condicionales explícitos, sin null checks y sin bloqueos innecesarios.