Utilizando Expressões Lambda

Postador por : at

Categoria : java


Primeiramente Expressões Lambda são funções anônimas, a diferença de uma função normal é que elas não possui nome então eles acaba sendo definidas diretamente muitas vezes em uma única linha, e serve para fornecer abstrações para problemas complexos e isso acaba limpando o código deixando o escopo menor.

Elas estão presentes nas linguagens como Java, C#, Python, Ruby e entre outras do mercado.

As principais vantagens sobre a utilização da expressão lambda:

  • Código mais simples;
  • Simplificar diversas operações em cima de uma coleção de dados;
  • Possui uma sintaxe extremamente simples.

E as poucas desvantagens:

  • Funções anônimas podem gerar problemas na hora de depurar o código;
  • Muitos argumentos em uma Expressão Lambda gera legibilidade mais difícil;

Agora vou mostrar alguns exemplos de uso de expressão lambda, utilizarei a linguagens Java para dar os exemplos.

package teste;

import java.util.ArrayList;
import java.util.Collection;

public class Funcionario {
 
    public String nome;
    public double salario;
    
    public Funcionario chamado(String nome){
      this.nome = nome;
      return this;
    }
    
    public Funcionario comSalario(double salario){
      this.salario = salario;
      return this;
    }
    
    public static void main(String[] args) {
      Collection<Funcionario> funcionarios = new ArrayList<Funcionario>();
      Funcionario joao = new Funcionario().chamado("João").comSalario(2000.00);
      Funcionario pedro = new Funcionario().chamado("Pedro").comSalario(3000.00);
      
      funcionarios.add(joao);
      funcionarios.add(pedro);
      
      for (Funcionario f : funcionarios) {
        System.out.println(f.nome + ": " + f.salario);
      }
      
      funcionarios.forEach((Funcionario f) -> System.out.println(f.nome + ": " + f.salario));
      
    }
}

Linhas 29 até 33: Usamos for para iterar os índices do ArrayList funcionarios

Linha 35: A partir do Java 8 podemos utilizar o método forEach da interface Iterable, da qual herda a classe ArrayList, para iterar os índices de uma coleção. Esse método recebe como parâmetro a interface funcional Consumer, que possui o método abstrato accept. Ao passarmos para o método forEach essa expressão lambda, estamos declarando que o método accept deve ser chamado, recebendo como parâmetro a instância de Funcionario f e realizando a impressão do nome e salário de cada funcionário. O método forEach se encarregará de executar essa ação repetidas vezes, enquanto existirem itens no ArrayList.

A seguir mostrarei como criar uma interface funcional, Condicao que contém o método test.

package teste;

public interface Condicao<T> {
    boolean teste(T t);
  }

Linhas 3 até 7: Declaramos a interface funcional genérica Condicao. O uso de Generics na declaração desta interface nos permite informar um tipo genérico para o método teste.

Linha 5: Aqui temos o método abstrato teste que recebe T, um tipo genérico, e retorna um boolean.

Usaremos essa interface no método getFuncionarios, da classe FolhaDePagamento, para filtrar os funcionários a partir de uma condição.

package teste;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class FolhaDePagamento {

    public List<Funcionario> getFuncionarios(Condicao<Funcionario> condicao) {
        Collection<Funcionario> funcionarios = new ArrayList();
        Funcionario joao = new Funcionario().chamado("João").comSalario(2000.00);
        Funcionario pedro = new Funcionario().chamado("Pedro").comSalario(3000.00);
        
        funcionarios.add(joao);
        funcionarios.add(pedro); 

        List<Funcionario> funcionariosComCondicao = new ArrayList<>();

        funcionarios.forEach(f -> {
            if (condicao.teste(f)){
                funcionariosComCondicao.add(f);
            }
        });

        return funcionariosComCondicao;
    }
    
}

Linhas 7 até 27: Declaramos a classe FolhaDePagamento, que contém o método getFuncionarios. Esse método retornará uma lista de funcionários selecionados a partir de uma condição.

Linhas 9 e 15: Criamos uma lista com alguns funcionários que usaremos como base. Em um exemplo mais próximo do mundo real essa lista poderia vir de um banco de dados. Logo após está a lista funcionariosComCondicao, que vai armazenar os funcionários que atendem a condição especificada.

Linha 19: Aqui usamos uma expressão lambda para receber cada funcionário da lista funcionários, submetendo-os ao método Condicao.teste. Se esse método retornar true, o funcionário é adicionado na lista funcionariosComCondicao.

Linha 25: Retornamos a lista dos funcionários que atendem a condição.

Na interface Condicao o teste é um método abstrato, que não possui corpo. O que de fato esse método faz será implementado por alguma classe que herde de Condicao ou, como veremos a seguir, por uma expressão lambda.

  public static void main(String[] args) {
        FolhaDePagamento folhaDePagamento = new FolhaDePagamento();
        List<Funcionario> funcionarios = folhaDePagamento.getFuncionarios(
            f -> f.salario <= 2500.00F);
        
        funcionarios.forEach(f -> System.out.println(f.nome));

    }

Linha 30: Invocamos o método getFuncionarios atribuindo seu retorno a lista funcionarios.

Linha 31: Usamos uma expressão lambda para invocar o método teste da classe Condicao. Como parâmetro passamos um objeto do tipo funcionário, e como corpo criamos uma expressão lógica, que retorna true ou false.

Linha 33: Percorremos os itens do ArrayList que contém os funcionários retornados por getFuncionarios, aqueles com salários menores ou iguais a 2500.00F, imprimindo seus nomes.

Com o Java 8, além do suporte as expressões lambda, foi introduzida a API Streams, que simplifica a realização de tarefas comuns relacionadas a coleções. No exemplo abaixo usamos algumas das funções da classe Stream, do pacote java.util.stream, para filtrar um grupo de funcionários, utilizando para isso uma condição, e retornar uma lista contendo seus nomes.

    public static void main(String[] args) {
        FolhaDePagamento folhaDePagamento = new FolhaDePagamento();
        List<Funcionario> funcionarios = folhaDePagamento.getFuncionarios(
            f -> f.salario <= 2500.00F);
        
        funcionarios.forEach(f -> System.out.println(f.nome));

        List<String> nomeFuncionarios = funcionarios.stream()
        .filter(f -> f.salario > 2500.00F)
        .map(f -> f.nome)
        .collect(Collectors.toList());
    }
}

Linha 36: Usamos o método stream para retornar um objeto do tipo Stream a partir da lista de funcionários. Assim poderemos acessar os métodos da classe Stream.

Linha 37: Com o método filter filtramos os funcionários que atendem a condição salário > 2500.00F, usando para isso uma expressão lambda.

Linha 38: Declaramos para o método map, a partir de uma expressão lambda, que usaremos o nome de cada funcionário para preencher a lista de retorno.

Linha 39: Retornamos a lista de nomes com o método collect. A transformação desse retorno em uma lista é facilitada pelo método toList da classe Collectors.

Espero que esses exemplos tenha esclarecido suas duvidas sobre o que é expressões lambda, e por favor se tiver alguma sugestão de como fazer melhor estou aberto a ouvi-lo pois estou aprendendo ainda e toda ajuda é bem vinda =).