Desafio Java

Calculadora funcional com interface personalizada e captura

Enunciado

Crie uma interface funcional Operacao com um método int aplicar(int a, int b). Implemente uma calculadora que recebe duas operações via lambda (soma e multiplicação) e as aplica a dois números. Além disso, capture uma variável fator para criar uma operação que multiplica por um valor fixo.

Requisitos

  • Defina @FunctionalInterface para Operacao.
  • Use lambdas para soma e multiplicação.
  • Capture uma variável fator (efetivamente final) em uma lambda.
  • Imprima os resultados no formato especificado.

Código inicial

public class CalculadoraFuncional {
    @FunctionalInterface
    interface Operacao {
        int aplicar(int a, int b);
    }

    public static void main(String[] args) {
        // Seu código aqui
    }
}

Saída esperada

Soma: 7
Multiplicação: 12
Multiplicado por 3: 15
Ver dica

Para capturar fator, defina int fator = 3; antes da lambda e use x -> x * fator.

Mostrar solução
public class CalculadoraFuncional {
    @FunctionalInterface
    interface Operacao {
        int aplicar(int a, int b);
    }

    public static void main(String[] args) {
        // Lambdas para operações básicas
        Operacao soma = (a, b) -> a + b;
        Operacao multiplicacao = (a, b) -> a * b;

        System.out.println("Soma: " + soma.aplicar(3, 4));
        System.out.println("Multiplicação: " + multiplicacao.aplicar(3, 4));

        // Captura de variável efetivamente final
        int fator = 3;
        Operacao multiplicaPorFator = (a, b) -> a * fator; // b ignorado
        System.out.println("Multiplicado por 3: " + multiplicaPorFator.aplicar(5, 0));
    }
}

Passo a passo

  1. Declaramos a interface funcional Operacao com a anotação @FunctionalInterface, que garante um único método abstrato aplicar.
  2. Criamos a lambda soma que recebe dois inteiros e retorna a soma. Ela implementa Operacao.
  3. Criamos a lambda multiplicacao que retorna o produto dos dois inteiros.
  4. Chamamos aplicar em cada operação e imprimimos os resultados formatados.
  5. Definimos a variável local fator com valor 3. Ela é efetivamente final (não é reatribuída).
  6. Criamos a lambda multiplicaPorFator que ignora o segundo parâmetro e multiplica o primeiro por fator. Capturamos fator do escopo externo.
  7. Chamamos aplicar(5, 0) e imprimimos o resultado.

Por que funciona

A anotação @FunctionalInterface permite que o compilador verifique se a interface tem exatamente um método abstrato, o que é pré-requisito para lambdas. As lambdas soma e multiplicacao fornecem implementações concisas para Operacao. A lambda multiplicaPorFator captura a variável fator, que é efetivamente final (não modificada após a inicialização). Isso é exigido pelo Java para evitar inconsistências em ambientes concorrentes e garantir que o valor capturado seja estável.

Erros comuns

  • Tentar modificar fator após a lambda: fator = 4; causaria erro de compilação: "local variables referenced from a lambda expression must be final or effectively final".
  • Esquecer a anotação @FunctionalInterface: não é obrigatória, mas sem ela o compilador não avisa se alguém adicionar outro método abstrato, quebrando as lambdas.
  • Usar b na lambda que ignora o segundo parâmetro: se escrever (a, b) -> a * fator, o parâmetro b não é usado, mas isso é permitido. No entanto, se a intenção for ignorar, pode-se usar _ (a partir do Java 21) ou simplesmente não usar.
  • Confundir method reference com lambda: Operacao::soma não funcionaria aqui porque soma não é um método estático acessível dessa forma; seria necessário Integer::sum se fosse o caso.

Outra forma de resolver

Usar method references para operações da classe Integer:

Operacao soma = Integer::sum;
Operacao multiplicacao = (a, b) -> a * b; // não há method reference direto para multiplicação

Method references são preferíveis quando já existe um método estático ou de instância que faz exatamente o que a lambda faria.

Saída esperada

Soma: 7
Multiplicação: 12
Multiplicado por 3: 15