DEV Community

Cover image for Expressão Lambda - Parte II (Quando e como usar)
Carolina Dias Fonseca
Carolina Dias Fonseca

Posted on • Edited on

Expressão Lambda - Parte II (Quando e como usar)

Parte 02 de 03 da documentação Oracle sobre Expressão Lambda :)

Um problema com classes anônimas é que, se sua implementação for muito simples (sendo uma interface que contém um único método, por exemplo), a sua sintaxe pode parecer incerta e não muito clara.

Nesses casos, normalmente você está tentando passar funcionalidade como argumento para outro método, semelhante a iniciar uma ação quando alguém clica num botão.

As expressões lambdas te permitem fazer isso, trabalhar uma funcionalidade como argumento ou código como dado de informação.

No artigo anterior sobre Classe Anônima, mostramos como implementar uma classe sem a necessidade de se criar uma classe com um único método e implementar/estender outra classe para seguir com a construção da sua aplicação.

A expressão Lambda te permite fazer a mesma coisa, porém de uma forma mais conscisa ainda.

Caso de Uso Ideal para Expressão Lambda

Vamos supor que você está criando uma aplicação de rede social. Você quer criar uma feature que permite o administrador a realizar determinada ação, como, por exemplo, enviar uma mensagem para membros que satisfaçam determinada condição.

UML

Cada pessoa nessa rede social é representada pela classe Pessoa:

public class Pessoa {
    public enum GENERO{
        MASCULINO, FEMININO
    }
    String nome;
    LocalDate nascimento;
    GENERO genero;
    String email;
    public int getIdade(){//...}    
    public void printPessoa(){//...}

}
Enter fullscreen mode Exit fullscreen mode

Suponha que os membros dessa rede social estão armazenado numa List<Pessoa>

Passo 01: Criar métodos que encontrem membros que atendem a uma certa característica

Uma forma muito simples de fazer essa filtragem é criar um método para cada tipo de filtro que se precisa fazer, como para gênero ou idade.

Exemplo:

public static void printPessoasMaioresQue(List<Pessoa> relacao, int idade){
    for(Pessoa p : relacao){
        if(p.getIdade() >= age){
            p.printPessoa();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Esse método é muito frágil por conta da quantidade de atualizações que a aplicação ainda passará por conta de inclusão de novos membros.

Outro ponto, suponha que você atualizou a sua aplicação e alterou a estrutura da classe Pessoa que passa a conter mais variáveis, talvez isso impacte em todos os lugares em que você instanciou a classe de forma que o algoritmo que usa essa informação agora haja de forma diferente.

E, por fim, é um método restritivo, e se você precisar mostrar em tela usuários mais novo?

Passo 02: Criar métodos de busca mais generalizados

O método abaixo é mais generalizado do que o anterior:

public static void printPessoasComIdadeEntre(List<Pessoa> relacao, int menor, int maior){
    for(Pessoa p : relacao){
        if(menor <= p.getIdade() && p.getIdade() >= maior){
            p.printPessoa();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Mas e se você quiser mostrar em tela membros de determinado gênero ou a combinação de idade e gênero?

Ou se você quiser incluir atributos como status de relacionamento ou localização?

Apesar do método acima ser mais genérico do que o anterior, ter que criar um método para cada possibilidade de busca ainda deixa o código frágil.

Ao invés disso você pode separar as especificações de critério de busca do que você quer busca em classes separadas.

Passo 03: Especificar o código de critério de busca em uma classe local

O método abaixo mostra em tela membros que atendem determinado critério de busca que você especificar:

public static void printPessoas(List<Perssoa> relacao, VerificaPessoa criterio){
    for(Pessoa p: relacao){
        if(criterio.buscaPessoa(p)){
            p.printPessoa();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Esse método verifica cada Pessoa que consta na List relacao, se a pessoa atender o critério passado em criterio.buscaPessoa(p) então o método mostra em tela a pessoa.

Para especificar o critério de busca, você implementa uma interface chamada VerificaPessoa

interface VerificaPessoa{
    boolean buscaPessoa(Pessoa p);
}
Enter fullscreen mode Exit fullscreen mode

A classe que fará uso desse método, faz a implementação da interface, exemplo:

class VerificaPessoaElegivelParaExercito implements VerificaPessoa{
    public boolean buscaPessoa(Pessoa p){
        return p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25;
    }
}
Enter fullscreen mode Exit fullscreen mode

Para usar esse classe, você criar uma instância e invoca o printPessoa:

printPessoa(relacao, new VerificaPessoaElegivelParaExercito())
Enter fullscreen mode Exit fullscreen mode

Apesar dessa estrutura ser menos frágil, você não precisa reescrever métodos se você alterar a classe Pessoa, você ainda tem código adicional: uma interface nova e uma classe local para cada busca que você pretende fazer na sua aplicação.

Passo 04: Especificar o critério de busca numa classe anônima

printPessoa(relacao, new VerificaPessoa(){
    public boolean buscaPessoa(Pessoa p){
        return p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25;
    }
})
Enter fullscreen mode Exit fullscreen mode

Essa forma reduz a quantidade de linhas de código porque você não precisa criar uma nova classe cada busca que você precisa realizar.

Entretanto, a sintaxe de uma classe anônima é volumoso considerando que a interface VerificaPessoa só contém um método. Nesse caso, você pode usar uma expressão lambda ao invés da classe anônima.

Passo 05: Especificar o critério de busca numa expressão lambda

A interface VerificaPessoa é uma interface funcional. Uma classe funcional é aquela que contém apenas um método abstrato.

Exatamente por ela ser uma classe funcional que contém um único método abstrato, você pode omitir o nome do método quando você a implementa. Ao fazer isso, você estará implementando uma expressão lambda ao invés de uma classe anônima.

printPessoa(relacao,(Pessoa p)-> p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25);
Enter fullscreen mode Exit fullscreen mode

Você ainda pode usar uma interface funcional padrão no lugar de criar uma interface, o que ajuda a reduzir a criação de código.


interface Predicate<Pessoa>{
    boolean buscaPessoa(Pessoa p);  
}

public static void printPessoaComPredicate(List<Pessoa> relacao, Predicate<Pessoa> criterio){
    for(Pessoa p : relacao){
        if(criterio.buscaPessoa(p){
            p.printPessoa();
        }
    }
}

printPessoaComPredicate(relacao, p -> p.getGenero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.getIdade() <= 25);
Enter fullscreen mode Exit fullscreen mode

Fonte: Documentação Oracle
Colaboração: Silvair Soares

Top comments (5)

Collapse
 
silvairsoares profile image
Silvair L. Soares • Edited

Parabéns, excelentes dicas. Trabalho com dotnet e c# e aqui as suas dicas também são perfeitamente válidas ( System.Linq.Expressions.Expression<Func<T>> e System.Predicate<T>).

Uma pequena observação (que obviamente não diminui em nada a importância das suas dicas): O primeiro parágrafo ficou inconclusivo.

"Um problema com classes anônimas é que se a implementação da sua classe anônima é muito simples sendo uma interface que contém um único método."

Um problema com... é que se... ???

Collapse
 
diariodeumacdf profile image
Carolina Dias Fonseca

Hey Silvair, beleza? Primeiramente, obrigada pelo feedback e sobre o parágrafo, rapaz, vou lhe ser sincera que fiquei (ainda estou) refletindo a melhor forma de escrever esse parágrafo, a doc da Oracle nesse trecho é beem confusa para mim... Tem alguma sugestão para eu editar o texto?

Collapse
 
silvairsoares profile image
Silvair L. Soares • Edited

E aí, blz. A documentação da Oracle foi bem mal escrita neste ponto mesmo.

Segundo a oracle (then the syntax of anonymous classes may seem unwieldy and unclear) o problema das classes anônimas é que sua sintaxe pode parecer complicada e confusa. Então, eu somente emendaria os dois primeiros parágrafos, e removeria algumas redundâncias que a Oracle incluiu em sua doc.

No seu resumo, a conclusão do assunto abordado no primeiro parágrafo, ficou no início do segundo. Eu deixaria mais ou menos assim:

“Um problema com classes anônimas é que, se sua implementação for muito simples (sendo uma interface que contém um único método, por exemplo), a sua sintaxe pode parecer incerta e não muito clara.

Nesses casos, normalmente você está tentando passar funcionalidade como argumento para outro método, semelhante a iniciar uma ação quando alguém clica num botão.”

Thread Thread
 
diariodeumacdf profile image
Carolina Dias Fonseca • Edited

Gostei, ficou beeem melhor, vou alterar com a sua sugestão ;) de novo: obrigada pelo feedback e pela colaboração! Alterei lá e achei válido te incluir na colaboração <3

Thread Thread
 
silvairsoares profile image
Silvair L. Soares • Edited

Que bom que gostou!
Nem precisava me incluir, o mérito é todo seu. Estamos todos no mesmo barco, eu estou aprendendo sempre também.
Um abraço.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.