Programação Funcional em Java: Nível Avançado
\n\nA programação funcional (PF) em Java deixou de ser apenas uma tendência da moda — é uma parte essencial da linguagem moderna desde a versão 8. Para desenvolvedores que dominam lambdas básicas e Stream API, chega a fase em que é necessário compreender conceitos mais profundos: mônadas, composição de funções, avaliação preguiçosa e estruturas de dados imutáveis. Este artigo é dedicado a técnicas avançadas de programação funcional em Java que ajudam a escrever código mais limpo, previsível e seguro para paralelismo.
\n\n1. Composição de Funções e Currying
\n\nEm Java, as interfaces funcionais Function, Predicate e Consumer fornecem métodos padrão para composição: andThen() e compose(). Isso permite construir cadeias de transformações sem criar variáveis intermediárias.
Exemplo de composição:
\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 // Composição: 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\nCurrying é a transformação de uma função de vários argumentos em uma sequência de funções de um único argumento. Em Java, isso pode ser simulado com lambdas aninhadas:
\n\nimport java.util.function.Function;\n\npublic class CurryingExample {\n // Função normal\n static int add(int a, int b) { return a + b; }\n\n // Versão curried\n static Function<Integer, Function<Integer, Integer>> curriedAdd = a -> b -> a + b;\n\n public static void main(String[] args) {\n // Aplicação 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\nCurrying é útil para criar funções especializadas baseadas em padrões gerais, especialmente ao trabalhar com configurações.
\n\n2. Trabalho Avançado com Stream API
\n\nO Stream API em Java não é apenas uma substituição para loops. Compreender seus recursos avançados permite aumentar significativamente o desempenho e a legibilidade do código.
\n\n2.1. Streams paralelos e Spliterators
\nStreams paralelos usam ForkJoinPool. No entanto, sua eficiência depende do tamanho dos dados e da natureza das operações. Para controle personalizado do paralelismo, pode-se 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 // Criando um stream a partir de um iterador com 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 // Divisão mais simples\n int mid = current + 50;\n if (mid >= 100) return null;\n Spliterator<Integer> prefix = new RangeSpliterator(curre