Interfaces Funcionais Padrão do Java
Com a introdução das expressões lambda no Java 8, a linguagem também ganhou um conjunto de interfaces funcionais padrão, localizadas no pacote java.util.function
. Em vez de criar nossas próprias interfaces para cada pequena tarefa (como Operationable
nos exemplos anteriores), podemos reutilizar essas interfaces genéricas.
Elas são os "blocos de construção" da programação funcional em Java e são usadas extensivamente em APIs modernas, como a Stream API. Vamos conhecer as mais importantes.
Interface Predicate: O "Avaliador"
Pense em um Predicate<T>
como um especialista em responder a uma pergunta de "sim ou não" sobre um objeto. Ele representa uma condição ou um teste.
- Assinatura:
boolean test(T t)
- Função: Recebe um objeto do tipo
T
e retornatrue
oufalse
. - Uso Comum: Filtrar coleções.
import java.util.function.Predicate;
public class LambdaApp {
public static void main(String[] args) {
// Este Predicate testa se um número é positivo.
Predicate<Integer> isPositive = x -> x > 0;
System.out.println(isPositive.test(5)); // true
System.out.println(isPositive.test(-7)); // false
}
}
Interface Function: O "Transformador"
Uma Function<T, R>
é como uma máquina de conversão. Ela pega um objeto de um tipo T
e o transforma em um objeto de outro tipo R
.
- Assinatura:
R apply(T t)
- Função: Recebe um objeto do tipo
T
e retorna um objeto do tipoR
. - Uso Comum: Mapear dados de um formato para outro.
import java.util.function.Function;
public class LambdaApp {
public static void main(String[] args) {
// Esta Function converte um Integer em uma String formatada.
Function<Integer, String> convertToString = x -> String.valueOf(x) + " dólares";
System.out.println(convertToString.apply(10)); // "10 dólares"
}
}
Interface Consumer: O "Consumidor"
Um Consumer<T>
"consome" um objeto para realizar uma ação, mas não retorna nada. Sua principal função é executar um efeito colateral, como imprimir algo na tela.
- Assinatura:
void accept(T t)
- Função: Recebe um objeto do tipo
T
e não retorna valor (void
). - Uso Comum: Executar uma ação para cada elemento de uma coleção (ex:
forEach
).
import java.util.function.Consumer;
public class LambdaApp {
public static void main(String[] args) {
// Este Consumer recebe um Integer e o imprime no console.
Consumer<Integer> printer = x -> System.out.printf("%d dólares\n", x);
printer.accept(600); // Imprime "600 dólares"
}
}
Interface Supplier: O "Fornecedor"
Um Supplier<T>
é como uma fábrica. Ele não recebe nenhum argumento, mas tem a responsabilidade de fornecer (criar ou obter) um objeto do tipo T
.
- Assinatura:
T get()
- Função: Não recebe nada e retorna um objeto do tipo
T
. - Uso Comum: Criar novos objetos (padrão de fábrica) ou adiar a execução de uma computação.
import java.util.Scanner;
import java.util.function.Supplier;
public class LambdaApp {
public static void main(String[] args) {
// Este Supplier é uma "fábrica" que cria um novo usuário a partir da entrada do console.
Supplier<User> userFactory = () -> {
Scanner in = new Scanner(System.in);
System.out.print("Digite o nome: ");
String name = in.nextLine();
return new User(name);
};
User user1 = userFactory.get(); // Chama a fábrica para criar o primeiro usuário
User user2 = userFactory.get(); // Chama a fábrica novamente para criar o segundo
System.out.println("Nome do user1: " + user1.getName());
System.out.println("Nome do user2: " + user2.getName());
}
}
// Classe User do exemplo anterior...
Operadores: Casos Especiais
Existem também especializações das interfaces funcionais para quando os tipos de entrada e saída são os mesmos.
Interface UnaryOperator: O "Modificador"
É um caso especial de Function<T, R>
onde T
e R
são o mesmo tipo. Ele recebe um objeto e retorna um objeto do mesmo tipo.
- Assinatura:
T apply(T t)
- Função: Recebe um
T
e retorna umT
.
import java.util.function.UnaryOperator;
public class LambdaApp {
public static void main(String[] args) {
// Este UnaryOperator recebe um Integer e retorna seu quadrado (outro Integer).
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25
}
}
Interface BinaryOperator: O "Combinador"
Recebe dois objetos do mesmo tipo T
e os combina para produzir um resultado, também do tipo T
.
- Assinatura:
T apply(T t1, T t2)
- Função: Recebe dois
T
e retorna umT
.
import java.util.function.BinaryOperator;
public class LambdaApp {
public static void main(String[] args) {
// Este BinaryOperator recebe dois Integers e retorna sua multiplicação (outro Integer).
BinaryOperator<Integer> multiply = (x, y) -> x * y;
System.out.println(multiply.apply(3, 5)); // 15
}
}
Resumo
Predicate<T>
: Testa um objeto e retornaboolean
.Function<T, R>
: Transforma umT
em umR
.Consumer<T>
: Executa uma ação com umT
e não retorna nada.Supplier<T>
: Não recebe nada, mas fornece umT
.UnaryOperator<T>
: Recebe umT
e retorna umT
.BinaryOperator<T>
: Recebe doisT
e retorna umT
.