A programação funcional é um paradigma de programação baseado em um modelo
de computação criado pelo matemático Alonzo Church, denominado Cálculo Lambda
.
Nesse paradigma existem basicamente funcões puras, que podem utilizar a
composição para criar funcões mais complexas.
As expressões lambdas foram inicialmente incluidas no Java 8 (2014).
Essas expressões são implementadas através de métodos abstratos únicos
(Single Abstract Method) com a annotation @FunctionalInterface ou usando as interfaces Function, Predicate, UnaryOperator
dentre outras. Essas interfaces estão disponíveis
através do pacote java.util.function
As Functions recebem apenas um parâmetro, realizam a operação e geram o output.
O tipo de entrada não precisa ser o mesmo da saída.
É permitido fazer composição da Function com outras interfaces funcionais,
usando .andThen, .composition
e outros.
Estrutura
...
Function<TipoEntrada, TipoSaida> nomeDaFunction = parametro -> operação;
Exemplo de implementação de uma Function:
Para realização operações com a Function basta utilizar o .apply(parâmetro)
.
Esse tipo de interface recebe como parâmetro apenas o tipo de entrada.
O retorno dessa interface é sempre booleano.
No caso do Predicate, ao invés de utilizar .apply(parâmetro)
, é utilizado . test(parâmetro)
. É possível utilizar or
, and
e negate
em conjunto com
a operação.
Estrutura:
...
Predicate<Tipo da entrada> nomeDoPredicate = parâmetro -> operação;
Implementação:
Processa os dados consumidos e a saida é void. No Consumer a forma de passar
o parâmetro é através do método .accept(parâmetro)
. E, assim como as
demais interfaces funcionais, também aceita composição.
Geralmente utilizado para processar dados de grandes coleções.
Estrutura:
...
Consumer<Tipo que será processado> nomeConsumer = parâmetro -> operação;
Implementação:
Essa interface seria o contrário do que é um Consumer, ela ao invés de
consumir os dados, ela irá gerar algo. É uma forma rápida de criar grandes
coleções e recuperar/gerar dados. O valor gerado pode ser conferido através
do método .get()
.
Estrutura:
Supplier<Tipo de saida> nomeSupplier = () -> operação;
Implementação:
Aceita um parâmetro de entrada, e seu tipo deve ser o mesmo da saida.
Estrutura:
UnaryOperator<Tipo entrada e saida> nome = (parametro) -> operação;
Implementação:
Os tipos de entrada são os mesmos de saída. Aceita dois parâmetros de entrada.
Estrutura:
BinaryOperator<Tipo entrada, tipo saida> nome = (primeiro_parametro, segundo_parametro) -> operação;
Implementação:
Interface que aceita mais de um parâmetro (diferente da Function que aceita um), e inclusive, os tipos desses parâmetros podem ser diferentes.
Estrutura:
BiFunction<Tipo da primeira entrada, tipo da segunda, tipo resultado>
nomeDaFunction = (primeiro_parametro, segundo) -> operação;
Implementação: