DEV Community

Alex Reis
Alex Reis

Posted on

Interfaces Funcionais

Uma interface funcional é definida como uma interface que não é declarada como selada (sealed) e pode conter métodos default e static mas possui exatamente um método abstrato (além dos métodos da classe Object).

Além da maneira tradicional (declarando e instanciando uma classe), instâncias de interfaces funcionais podem ser criadas de forma mais concisa utilizando expressões lambda e referências de método.

Existe ainda a anotação @FunctionalInterface que serve para indicar que uma interface deve ser funcional. Se uma interface for anotada dessa forma, mas não atender aos requisitos (como ter mais de um método abstrato), o compilador gerará um erro. No entanto, ela é opcional.

Para evitar que tenhamos que criar uma nova interface toda vez que precisarmos de uma função simples, o Java fornece um pacote padrão com as interfaces funcionais mais comuns (java.util.function). As quatro principais são:

  1. Predicate<T>: Recebe um argumento e retorna um boolean.
    • Uso: Filtros e condicionais.
    • Método abstrato: test(T t)
  2. Consumer<T>: Recebe um argumento e não retorna nada (void).
    • Uso: Impressão de dados, gravação em banco, efeitos colaterais.
    • Método abstrato: accept(T t)
  3. Function<T, R>: Recebe um argumento do tipo T e retorna um resultado do tipo R.
    • Uso: Transformação de dados (mapeamento).
    • Método abstrato: apply(T t)
  4. Supplier<T>: Não recebe argumentos e retorna um resultado do tipo T.
    • Uso: Factories, geração preguiçosa (lazy) de dados.
    • Método abstrato: get()

Exemplo prático

import java.util.function.Predicate;

// 1. Definindo uma Interface Funcional Customizada
@FunctionalInterface
interface ConversorTexto {
    String converter(String texto); // Único método abstrato

    // Método default não quebra a regra de interface funcional
    default void imprimirLog() {
        System.out.println("Executando conversão...");
    }
}

public class AulaInterfaces {
    public static void main(String[] args) {

        // --- USO DA INTERFACE CUSTOMIZADA ---

        // Implementação clássica (Classe Anônima) - O jeito antigo
        ConversorTexto gritarClassico = new ConversorTexto() {
            @Override
            public String converter(String texto) {
                return texto.toUpperCase() + "!!!";
            }
        };

        // Implementação com Lambda - O jeito moderno
        // O compilador infere que 's' é o argumento de 'converter'
        ConversorTexto gritarLambda = s -> s.toUpperCase() + "!!!";

        System.out.println(gritarLambda.converter("bom dia")); 
        // Saída: BOM DIA!!!


        // --- USO DE INTERFACE PADRÃO (Predicate) ---

        // Predicate verifica uma condição
        Predicate<Integer> isPar = numero -> numero % 2 == 0;

        validarNumero(10, isPar); // Passando comportamento como argumento
        validarNumero(7, isPar);
    }

    // Método que aceita um comportamento (Predicate) como parâmetro
    public static void validarNumero(int n, Predicate<Integer> regra) {
        if (regra.test(n)) {
            System.out.println(n + " passou na regra.");
        } else {
            System.out.println(n + " não passou na regra.");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Leituras Complementares

  • Documentação Oficial Oracle: Pacote java.util.function.
  • Livro: "Java 8 in Action" (Raoul-Gabriel Urma) - Capítulo sobre Lambdas.
  • Artigo: "Functional Interfaces in Java" no Baeldung.

Top comments (0)