Programação Funcional em Java: de Lambdas ao Stream API
Programação Funcional (PF) é um paradigma no qual os cálculos são representados como avaliação de funções matemáticas, evitando a mudança de estado e a mutação de dados. Por muito tempo, Java permaneceu uma linguagem estritamente orientada a objetos, mas com o lançamento do Java 8 (2014), deu um enorme passo em direção à PF, adicionando expressões lambda, interfaces funcionais e o Stream API. Hoje, essas ferramentas são indispensáveis para escrever código conciso, legível e seguro para paralelismo.
Neste artigo, vamos explorar os conceitos-chave da programação funcional em Java: desde os fundamentos das lambdas até o processamento avançado de coleções com o Stream API. O material é voltado para desenvolvedores que já conhecem a sintaxe básica do Java, mas desejam dominar abordagens modernas.
1. Expressões Lambda e Interfaces Funcionais
Uma expressão lambda é uma função anônima (um método sem nome) que pode ser passada como argumento ou armazenada em uma variável. Em Java, uma lambda implementa uma interface funcional — uma interface com exatamente um método abstrato (SAM — Single Abstract Method).
Sintaxe:
(parâmetros) -> { corpo }Exemplo: em vez de criar uma classe anônima para um comparador:
// Jeito antigo (Java 7)Comparator<String> comparator = new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); }};
// Jeito novo (Java 8+)Comparator<String> comparatorLambda = (s1, s2) -> s1.length() - s2.length();Principais interfaces funcionais em Java (pacote java.util.function):
Predicate<T>— verifica uma condição (métodotest).Function<T, R>— transforma um objeto do tipo T em R (métodoapply).Consumer<T>— executa uma ação sobre um objeto (métodoaccept).Supplier<T>— fornece um objeto sem parâmetros de entrada (métodoget).
Exemplo com Predicate:
Predicate<Integer> isEven = n -> n % 2 == 0;System.out.println(isEven.test(4)); // trueSystem.out.println(isEven.test(5)); // falseReferências a Métodos (Method References) — são uma forma abreviada de escrever lambdas quando o corpo consiste em uma única chamada de método:
// Lambdalist.forEach(item -> System.out.println(item));// Referência a métodolist.forEach(System.out::println);2. Stream API: Operações Básicas
O Stream API é uma ferramenta poderosa para processar sequências de dados (coleções, arrays, arquivos) de forma declarativa. Um Stream não armazena dados, apenas descreve um pipeline de operações. Os dados não são modificados — cada etapa cria um novo stream.
Criação de um Stream:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");Stream<String> stream = names.stream(); // sequencialStream<String> parallelStream = names.parallelStream(); // paraleloOperações intermediárias (retornam um novo Stream):
filter(Predicate)— seleciona elementos com base em uma condição.map(Function)— transforma cada elemento.sorted()/sorted(Comparator)— ordenação.distinct()— remove duplicatas.limit(n)— limita o número de elementos.
Operações terminais (finalizam o stream, retornam um resultado):
collect(Collectors.toList())— coleta em uma coleção.forEach(Con