Introduzido no Java 8, as expressões lambda representam uma forma concisa de definir funções anônimas. Esta característica, juntamente com as interfaces funcionais, transforma a forma como o código é escrito, promovendo uma programação mais funcional e declarativa.
Sintaxe Básica das Expressões Lambda
Uma expressão lambda é definida por uma lista de parâmetros, seguida pelo operador seta (->) e um corpo. Se o corpo contiver uma única instrução, as chaves e a palavra-chave return podem ser omitidas. O compilador frequentemente infere os tipos dos parâmetros, tornando o código mais limpo.
Exemplos de formas válidas:
// Sem parâmetros
() -> System.out.println("Executando tarefa");
// Um único parâmetro (parênteses opcionais)
mensagem -> System.out.println(mensagem);
// Dois parâmetros com retorno explícito
(a, b) -> {
int resultado = a + b;
return resultado;
};
// Dois parâmetros com retorno implícito
(a, b) -> a + b;
Regras de Sintaxe
- Zero ou mais parâmetros são permitidos.
- Com um único parâmetro e tipo inferido, os parênteses são opcionais.
- O corpo pode ser uma única expressão ou um bloco de código entre chaves.
- Para múltiplas instruções no corpo, as chaves são obrigatórias e um
returnexplícito é necessário se houver valor de retorno.
Interfaces Funcionais
Uma interface funcional é uma interface com exatamente um método abstrato. As expressões lambda só podem ser usadas em contextos que esperam uma interface funcional. A anotação @FunctionalInterface pode ser usada para garantir essa restrição em tempo de compilação.
@FunctionalInterface
public interface TransformadorString {
String aplicar(String texto);
}
Exemplo de uso com uma expressão lambda:
TransformadorString paraMaiusculas = s -> s.toUpperCase();
String resultado = paraMaiusculas.aplicar("java oito"); // "JAVA OITO"
Interfaces Funcionais Embutidas no Java 8
A biblioteca padrão fornece quatro interfaces funcionais genéricas fundamentais, localizadas no pacote java.util.function.
1. Consumer<T> - Consumidor
Representa uma operação que aceita um único argumento e não retorna resultado. É usado para produzir efeitos colaterais.
// Assinatura do método
void accept(T t);
// Exemplo de uso
Consumer<String> impressora = texto -> System.out.println("Log: " + texto);
impressora.accept("Operação concluída");
2. Supplier<T> - Fornecedor
Representa uma operação que não aceita argumentos e retorna um resultado. É usado para gerar ou fornecer valores.
// Assinatura do método
T get();
// Exemplo: Gerador de números aleatórios
Supplier<Double> geradorAleatorio = () -> Math.random();
Double valor = geradorAleatorio.get();
3. Function<T, R> - Função
Representa uma função que aceita um argumento do tipo T e produz um resultado do tipo R.
// Assinatura do método
R apply(T t);
// Exemplo: Converter String para seu tamanho
Function<String, Integer> calcularComprimento = s -> s.length();
int tamanho = calcularComprimento.apply("Lambda"); // 6
4. Predicate<T> - Predicado
Representa uma função que aceita um argumento do tipo T e retorna um valor booleano. É frequentemente usado para filtragem.
// Assinatura do método
boolean test(T t);
// Exemplo: Verificar se um número é par
Predicate<Integer> ehPar = numero -> numero % 2 == 0;
boolean teste = ehPar.test(4); // true
Exemplo Prático: Filtragem com Predicate
Abaixo, uma lista de palavras é filtrada para manter apenas as que têm mais de cinco caracteres.
List<String> palavras = Arrays.asList("lambda", "expressão", "java", "interface");
Predicate<String> comprimentoMaiorQueCinco = p -> p.length() > 5;
List<String> palavrasLongas = new ArrayList<>();
for (String palavra : palavras) {
if (comprimentoMaiorQueCinco.test(palavra)) {
palavrasLongas.add(palavra);
}
}
// Resultado: ["lambda", "expressão", "interface"]
Composição de Interfaces Funcionais
Essas interfaces possuem métodos padrão (como andThen, compose, negate) que permitem combinar operações, criando pipelines de processamento complexos de forma elegante.
Function<String, String> removerEspacos = s -> s.trim();
Function<String, String> converterMinusculas = s -> s.toLowerCase();
// Composição: primeiro remove espaços, depois converte para minúsculas
Function<String, String> limparEFormatar = removerEspacos.andThen(converterMinusculas);
String resultado = limparEFormatar.apply(" JAVA 8 "); // "java 8"