Programação Funcional em Java: Lambdas, Streams e Código Declarativo
A programação funcional no Java não é mais uma novidade, mas muitos desenvolvedores ainda não exploram todo o potencial das ferramentas introduzidas desde o Java 8. Vamos explorar como escrever código mais limpo, seguro e performático.
O Paradigma Funcional no Mundo Java
Tradicionalmente imperativo e orientado a objetos, o Java evoluiu para um ecossistema multi-paradigma. A programação funcional foca em o que deve ser feito, em vez de como fazer, priorizando imutabilidade e funções puras.
Lambda Expressions: O Início de Tudo
Lambdas permitem tratar funções como cidadãos de primeira classe, possibilitando passar blocos de código como argumentos para métodos.
// Antes (Classe Anônima)
Collections.sort(nomes, new Comparator<String>() {
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Depois (Lambda)
nomes.sort((a, b) -> a.compareTo(b));
// Melhor ainda (Method Reference)
nomes.sort(String::compareTo);Stream API: Processamento de Dados Elegante
Streams permitem processar coleções de dados de forma declarativa e paralela. Elas não alteram a fonte original, promovendo a imutabilidade.
Operações Comuns em Streams
List<String> resultado = pessoas.stream()
.filter(p -> p.getIdade() > 18) // Filtro
.map(Pessoa::getNome) // Transformação
.sorted() // Ordenação
.limit(10) // Curto-circuito
.collect(Collectors.toList()); // ColetaTrabalhando com Optional
O Optional foi introduzido para mitigar o famoso NullPointerException. Ele força o desenvolvedor a lidar explicitamente com a ausência de valor.
// Abordagem funcional com Optional
public String findUserEmail(Long id) {
return userRepository.findById(id)
.map(User::getEmail)
.orElse("email@naofornecido.com");
}Imutabilidade com Java Records
A imutabilidade é um pilar da programação funcional. Com os Records (introduzidos no Java 14/16), ficou muito mais fácil criar Data Transfer Objects (DTOs) imutáveis.
public record Product(String name, BigDecimal price) {
// Todos os campos são finais (final)
// Getter, equals, hashCode e toString gerados automaticamente
}Funções de Alta Ordem
Em Java, qualquer interface com apenas um método abstrato (Functional Interface) pode ser representada por um Lambda. As principais são:
- Predicate: Recebe um valor e retorna um booleano.
- Function: Recebe um valor e retorna outro.
- Consumer: Recebe um valor e não retorna nada.
- Supplier: Não recebe nada e retorna um valor.
Conclusão
Adotar práticas funcionais no Java resulta em código mais conciso, menos propenso a bugs de estado e mais fácil de testar. No entanto, o equilíbrio é chave: não force o uso de Streams em cenários onde um loop for simples seria mais legível.