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.
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(){//...}
}
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();
}
}
}
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();
}
}
}
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();
}
}
}
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);
}
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;
}
}
Para usar esse classe, você criar uma instância e invoca o printPessoa:
printPessoa(relacao, new VerificaPessoaElegivelParaExercito())
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;
}
})
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);
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);
Fonte: Documentação Oracle
Colaboração: Silvair Soares
Top comments (5)
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>>
eSystem.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... ???
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?
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.”
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
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.