DEV Community

Cover image for Introdução a Interfaces Funcionais
André Buarque
André Buarque

Posted on

Introdução a Interfaces Funcionais

O java 8 introduziu várias funcionalidades interessantes, como novas APIs de Stream, Optional, Date and Time e Functional Interfaces.

Este artigo tem o objetivo de fazer uma breve introdução sobre as Interfaces Funcionais e apresentar os tipos mais comuns disponíveis a partir do JDK 8.

Interfaces funcionais permitem trabalharmos com closures no Java. Closure é uma função que conhece o escopo na qual foi criada. Ou seja, a função tem acesso às suas variáveis e às variáveis e parâmetros da função externa que a criou. Com as interfaces funcionais, também é possível passarmos funções por parâmetros ou retorná-las.

Uma Interface Funcional deve obrigatoriamente possuir um e apenas um método abstrato, mas que pode ter outros métodos default.

Além disso, recomenda-se anotar a interface com @FunctionalInterface. Esta anotação irá ajudar o compilador a sinalizar um erro caso você tente adicionar mais de um método abstrato. Incluir essa anotação é opcional, desde que respeite a regra de ter apenas um método abstrato.

Exemplo:

@FunctionalInterface
public interface Operacao {
    int calcular(int a, int b);
}
Enter fullscreen mode Exit fullscreen mode

Para definir uma função a partir de uma interface, usamos expressões lambda. Lambda nada mais é do que uma função anônima, que não possui nome e nem está vinculada como um método de classe. Abaixo algumas diferentes formas de criar uma função usando expressão lambda:

  1. Com as chaves: quando o corpo da função tiver várias instruções.
Funcao fn = (a, b) -> {
    System.out.println("Realizando a soma..."); // instrução 1
    return a + b; // instrução 2
};
Enter fullscreen mode Exit fullscreen mode
  1. Sem as chaves: quando a função tiver apenas uma instrução. O resultado da instrução a + b será retornado na chamada da função.
Funcao fn = (a, b) -> a + b;
Enter fullscreen mode Exit fullscreen mode
  1. Sem os parênteses do argumento: quando há apenas 1 parâmetro na função, podemos omitir os parênteses.
Funcao fn = x -> x * 2;
Enter fullscreen mode Exit fullscreen mode

Assim, utilizando o exemplo acima da interface Operacao, criamos várias operações matemáticas utilizando lambdas:

Operacao soma = (a, b) -> a + b;
Operacao subtracao = (a, b) -> a - b;
Operacao divisao = (a, b) -> a / b;
Operacao multiplicacao = (a, b) -> a * b;

System.out.println(soma.calcular(1, 2)); // 3
System.out.println(subtracao.calcular(5, 1)); // 4
System.out.println(divisao.calcular(10, 5)); // 2
System.out.println(multiplicacao.calcular(2, 5)); // 10
Enter fullscreen mode Exit fullscreen mode

Analisando o exemplo acima, podemos notar que a mesma interface foi utilizada para várias operações matemáticas e o código ficou bem reduzido e mais legível!

Mas se você não quer ficar criando interfaces funcionais no seu projeto, também pode utilizar das já disponiveis no próprio Java. Vamos conhecer algumas?

java.util.function.Function<T, R>

Esta função recebe um parâmetro do tipo T e retorna um valor do tipo R.

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}
Enter fullscreen mode Exit fullscreen mode

Exemplo:

Function<Integer, Float> dividePorDois = x -> ((float) x / 2);

System.out.println(dividePorDois.apply(15)); // 7.5
Enter fullscreen mode Exit fullscreen mode

java.util.function.Consumer<T>

Esta função recebe um parâmetro do tipo T, mas não retorna nada.

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}
Enter fullscreen mode Exit fullscreen mode

Exemplo:

Consumer<String> exibeMensagem = str -> System.out.println(str);
// ou
Consumer<String> exibeMensagem2 = System.out::println;

exibeMensagem.accept("Hello World!"); // Hello World!
exibeMensagem2.accept("Hello World!"); // Hello World!
Enter fullscreen mode Exit fullscreen mode

Note que, como o método println possui a mesma assinatura de método da interface Consumer<String> (recebe uma String e não retorna nada) e há somente uma instrução na função, podemos passar a referência do próprio método println. Isso se chama Method Reference.

java.util.function.Supplier<T>

Esta função não recebe parâmetros e retorna um valor do tipo T.

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Enter fullscreen mode Exit fullscreen mode

Exemplo:

Supplier<LocalDate> dataAtual = () -> LocalDate.now();

System.out.println(dataAtual.get()); // 2021-06-02
Enter fullscreen mode Exit fullscreen mode

java.util.function.Predicate<T>

Esta função recebe um parâmetro do tipo T e retorna um valor booleano.

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}
Enter fullscreen mode Exit fullscreen mode

Exemplo:

Predicate<LocalDate> maiorDeIdade = dataNascimento -> {
    return dataNascimento.until(LocalDate.now(), ChronoUnit.YEARS) >= 18;
};

System.out.println(maiorDeIdade.test(LocalDate.of(1985, 10, 5))); // true
Enter fullscreen mode Exit fullscreen mode

As interfaces disponíveis no JDK abordadas neste artigo possuem especializações que permitem receber dois parâmetros (adicionando o prefixo Bi) ou trabalhar diretamente com tipos primitivos (adicionando o prefixo Int, Long ou Double). Exemplos: BiFunction, IntFunction, LongFunction e DoubleFunction.

Vimos os quatro principais tipos de interfaces funcionais. Recomendo que você explore as diversas opções que a JDK fornece e aplicá-las sempre que possível nos projetos.

Os exemplos utilizados neste artigo estão no repositório: https://github.com/andrebuarque/functional-interfaces-java.

Até logo!

Top comments (0)