Programación funcional en Java: nivel avanzado
\n\nLa programación funcional (PF) en Java ha dejado de ser simplemente una tendencia de moda: es una parte integral del lenguaje moderno desde la versión 8. Para los desarrolladores que han dominado las lambdas básicas y Stream API, llega la etapa en la que se requiere comprender conceptos más profundos: mónadas, composición de funciones, evaluación perezosa y estructuras de datos inmutables. Este artículo está dedicado a las técnicas avanzadas de programación funcional en Java que ayudarán a escribir código más limpio, predecible y seguro para la concurrencia.
\n\n1. Composición de funciones y currificación
\n\nEn Java, las interfaces funcionales Function, Predicate y Consumer proporcionan métodos por defecto para la composición: andThen() y compose(). Esto permite construir cadenas de transformaciones sin crear variables intermedias.
Ejemplo de composición:
\nimport java.util.function.Function;\n\npublic class FunctionComposition {\n public static void main(String[] args) {\n Function<String, String> trim = String::trim;\n Function<String, String> toUpper = String::toUpperCase;\n Function<String, String> addExclamation = s -> s + "!";\n\n // Composición: trim -> toUpper -> addExclamation\n Function<String, String> process = trim.andThen(toUpper).andThen(addExclamation);\n\n String result = process.apply(" hello world ");\n System.out.println(result); // HELLO WORLD!\n }\n}\n\nCurrificación es la transformación de una función de varios argumentos en una secuencia de funciones de un solo argumento. En Java, esto se puede emular con lambdas anidadas:
\n\nimport java.util.function.Function;\n\npublic class CurryingExample {\n // Función normal\n static int add(int a, int b) { return a + b; }\n\n // Versión currificada\n static Function<Integer, Function<Integer, Integer>> curriedAdd = a -> b -> a + b;\n\n public static void main(String[] args) {\n // Aplicación parcial\n Function<Integer, Integer> add5 = curriedAdd.apply(5);\n System.out.println(add5.apply(3)); // 8\n System.out.println(add5.apply(10)); // 15\n }\n}\n\nLa currificación es útil para crear funciones especializadas basadas en patrones generales, especialmente al trabajar con configuraciones.
\n\n2. Trabajo avanzado con Stream API
\n\nStream API en Java no es solo un reemplazo de los bucles. Comprender sus capacidades avanzadas permite aumentar significativamente el rendimiento y la legibilidad del código.
\n\n2.1. Streams paralelos y Spliterator
\nLos streams paralelos utilizan ForkJoinPool. Sin embargo, su eficiencia depende del tamaño de los datos y la naturaleza de las operaciones. Para un control personalizado del paralelismo, se puede usar Spliterator.
import java.util.Spliterator;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\npublic class CustomSpliterator {\n public static void main(String[] args) {\n // Creación de un stream a partir de un iterador con Spliterator personalizado\n Spliterator<Integer> spliterator = new Spliterator<>() {\n int current = 0;\n @Override\n public boolean tryAdvance(java.util.function.Consumer<? super Integer> action) {\n if (current < 100) {\n action.accept(current++);\n return true;\n }\n return false;\n }\n @Override\n public Spliterator<Integer> trySplit() {\n // División más simple\n int mid = current + 50;\n if (mid >= 100) return null;\n Spliterator<Integer> prefix = new RangeSpliterator(curre