Interfaces Funcionais Java - DoubleFunction
A partir da versão 8 do java foi introduzido o conceito de expressão lambda, que nada mais é que um bloco de código que pode receber parâmetros e também pode retornar um determinado valor.
Expressões lambdas são bem semelhantes a métodos, mas não precisam ter um nome e contém apenas o corpo da implementação.
Mas para que seja possível a utilização de lambdas, o java teve que introduzir o conceito de interfaces funcionais que por definição é uma interface que possui apenas um único método abstrato.
É importante lembrar que uma interface funcional pode possuir mais métodos, mas eles precisam ter uma implementação default.
Também podemos fazer com que o compilador do java garanta que determinada interface seja funcional. Com isso evitamos que nenhum outro desenvolvedor no futuro adicione outro método abstrato nessa interface e quebre o contrato de interface funcional, para isso basta adicionar a seguinte anotação na interface @FunctionalInterface
.
DoubleFunction
Antes de entrarmos em detalhes sobre a interface funcional DoubleFunction
aconselho você a dar uma olhada no meu outro post sobre a interface funcional Funcion
.
Mas o que difere a interface Function
para DoubleFunction
, sendo que poderíamos tipar a interface Function
com o tipo Double
.
Aqui vem a sacada de mestre dos engenheiros da JVM.
Como tipos primitivos não podem ser argumentos de tipos genéricos, e pelo fato de que tipos primitivos consomem menos memória, há versões da interface Function
para os tipos primitivos mais utilizados, como double
, int
e long
.
Segue a implementação da interface DoubleFunction
onde podemos observar que o parâmetro de entrada é do tipo double
primitivo, podendo customizar apenas o tipo de retorno.
@FunctionalInterface
public interface DoubleFunction<R> {
R apply(double value);
}
Para um maior entendimento de tudo que foi apresentado, vamos criar um cenário e implementar um exemplo para que o conhecimento aprendido neste post possa ser aplicado por você.
Cenário
Dado o valor total de uma venda, precisamos calcular a comissão do diretor, gerente, vendedor e assistente de prospecção.
-
Diretor - deve ter uma comissão de R$ 5,00 em vendas de até R$ 100,00, para vendas de até R$ 1.000,00 o diretor deve lucrar R$ 1,00 para cada R$ 30,00 vendidos, e para vendas acima de R$ 1.000,00 ele deve lucrar R$ 1,00 a cada R$ 25,00 vendidos.
-
Gerente - deve ter uma comissão de 9% em cima do valor total da venda.
-
Vendedor - deve ter uma comissão de 10% em cima do valor total da venda.
-
Assistente de prospecção - deve receber R$ 1,00 para vendas de até R$ 100,00, e para vendas acima de R$ 100,00 deve lucrar R$ 1,00 para cada R$ 50,00 vendidos.
Implementação
Quando estamos pensando em uma solução de implementação devemos estar cientes que nenhum cenário está escrito em pedra, em algum momento pode surgir uma nova regra ou uma mudança na regra existente. Tendo este pensamento e com o cenário mencionado anteriormente, acredito que a melhor opção de desenvolvimento seria a implementação de enum contendo os tipos de funções dos colaboradores o qual deve obrigar a implementação do cálculo da comissão, que nada mais é do que a implementação de uma interface funcional DoubleFunction
.
import java.util.function.DoubleFunction;
public enum Colaboradores {
DIRETOR(new CalculoComissaoDiretor()),
ASSISTENTE_PROSPECCAO(new CalculoComissaoAssistenteProspeccao()),
VENDEDOR(precoVenda -> precoVenda * 0.1),
GERENTE(precoVenda -> precoVenda * 0.09);
private final DoubleFunction<Double> calculoComissao;
Colaboradores(DoubleFunction<Double> calculoComissao) {
this.calculoComissao = calculoComissao;
}
public DoubleFunction<Double> getCalculoComissao() {
return calculoComissao;
}
}
Ao observarmos a implementação acima, estamos fazendo o uso de expressões lambdas na implementação do cálculo de comissão do vendedor e do gerente, já para o cálculo de comissão do diretor e do assistente de prospecção, onde a lógica para implementação contém um pouco mais de código, resolvi separar em outras duas classes, onde implemento a interface funcional DoubleFunction
.
import java.util.function.DoubleFunction;
public class CalculoComissaoAssistenteProspeccao implements DoubleFunction<Double> {
@Override
public Double apply(double precoVenda) {
if (precoVenda <= 100) {
return 1D;
}
return (double) ((int) precoVenda / 50);
}
}
import java.util.function.DoubleFunction;
public class CalculoComissaoDiretor implements DoubleFunction<Double> {
@Override
public Double apply(double precoVenda) {
if (precoVenda <= 100) {
return 5D;
} else if (precoVenda <= 1_000) {
return (double) ((int) precoVenda / 30);
}
return (double) ((int) precoVenda / 25);
}
}
Caso surja um novo cargo na empresa que deve receber comissões nas vendas, basta adicionar um novo tipo no enum e utilizar uma regra de cálculo já existente ou implementar uma nova. Feito isso garantimos que nenhum desenvolvedor crie um novo cargo sem implementar a regra de comissão.
Conclusão
Neste post, descrevemos a interface funcional DoubleFunction
que está presente na API do Java 8, e que ela pode ser usada como expresssão lambda.
O código fonte deste post está disponível neste repoistório no github.