Programação Funcional em Java: nível avançado
Programação Funcional (PF) é um paradigma onde os cálculos são tratados como avaliação de funções matemáticas, evitando mudanças de estado e dados mutáveis. Java, sendo uma linguagem orientada a objetos, a partir do Java 8 recebeu ferramentas poderosas para PF: expressões lambda, Stream API e interfaces funcionais. Em nível avançado, essas capacidades permitem escrever código mais conciso, seguro e expressivo, especialmente ao trabalhar com coleções, operações assíncronas e tratamento de erros.
Neste artigo, exploraremos técnicas avançadas de programação funcional em Java: composição de funções, mônadas (Optional e CompletableFuture), Stream API com processamento paralelo e currying.
1. Composição de funções e interfaces funcionais
Em Java, interfaces funcionais são interfaces com um único método abstrato (SAM). As mais usadas são: Function<T, R>, Predicate<T>, Consumer<T> e Supplier<T>. O uso avançado inclui composição de funções com os métodos andThen() e compose().
Exemplo: cadeia de transformações
import java.util.function.Function;
public class FunctionComposition { public static void main(String[] args) { // Função: string -> seu comprimento Function<String, Integer> lengthFunction = String::length; // Função: número -> seu quadrado Function<Integer, Integer> squareFunction = x -> x * x;
// Composição: string -> comprimento -> quadrado do comprimento Function<String, Integer> composedFunction = lengthFunction.andThen(squareFunction);
String text = "Hello, Java!"; int result = composedFunction.apply(text); // 12 * 12 = 144 System.out.println("Resultado: " + result); // 144 }}O método andThen() aplica a primeira função, depois a segunda. compose() faz o inverso: primeiro aplica a função passada, depois a primeira. Isso permite construir cadeias de transformação flexíveis e reutilizáveis.
Exemplo prático: filtragem e transformação de lista
import java.util.List;import java.util.function.Function;import java.util.function.Predicate;import java.util.stream.Collectors;
public class PredicateAndFunction { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie", "David", "Eve");
Predicate<String> startsWithA = s -> s.startsWith("A"); Function<String, String> toUpperCase = String::toUpperCase;
List<String> result = names.stream() .filter(startsWithA) .map(toUpperCase) .collect(Collectors.toList());
System.out.println(result); // [ALICE] }}2. Mônadas em Java: Optional e CompletableFuture
Mônada é um padrão que encapsula um valor e fornece operações para transformá-lo sem desempacotamento explícito. Em Java, as mônadas são implementadas através de Optional (para tratamento de null) e CompletableFuture (para operações assíncronas).
Optional como mônada
Os métodos map(), flatMap() e filter() permitem trabalhar encadeadamente com a possível ausência de valor.
import java.util.Optional;
public class OptionalMonad { public static void main(String[] args) { // Criando um Optional com valor não vazio Optional<String> optional = Optional.of("Hello");
// Cadeia de transformações String result = optional .filter(s -> s.length() > 3) // filtro .m