Usar expressões Lambda sempre que possível nas boas práticas de java?


52

Dominei recentemente a expressão Lambda que foi introduzida no java 8. Acho que sempre que estou usando uma interface funcional, costumo sempre usar uma expressão Lambda em vez de criar uma classe que implementa a interface funcional.

Isso é considerado uma boa prática? Ou são as situações em que o uso de um Lambda para uma interface funcional não é apropriado?


122
Para a pergunta "Está usando a técnica X sempre que possível, boas práticas", a única resposta correta é "não, use a técnica X não sempre que possível, mas sempre que sensata" .
Doc Brown

A escolha de quando usar um lambda (que é anônimo) versus outro tipo de implementação funcional (por exemplo, uma referência de método) é uma questão realmente interessante. Tive a oportunidade de conhecer Josh Bloch alguns dias atrás, e esse foi um dos pontos sobre os quais ele falou. Pelo que ele disse, eu entendo que ele planeja adicionar um novo item à próxima edição do Effective Java, que lida com essa pergunta exata.
precisa saber é o seguinte

@ DocBrown Isso deve ser uma resposta, não um comentário.
precisa saber é o seguinte

11
@ Gnasher729: definitivamente não.
Doc Brown

Respostas:


116

Há vários critérios que devem fazer você considerar não usar um lambda:

  • Tamanho Quanto maior o lambda, maior a dificuldade de seguir a lógica que o cerca.
  • Repetição É melhor criar uma função nomeada para lógica repetida, embora seja bom repetir lambdas muito simples que são separadas.
  • Nomeação Se você puder pensar em um ótimo nome semântico, use-o, pois ele adiciona muita clareza ao seu código. Eu não estou falando nomes como priceIsOver100. x -> x.price > 100é tão claro quanto esse nome. Quero dizer que nomes assim isEligibleVotersubstituem uma longa lista de condições.
  • Aninhamento Lambdas aninhadas são muito, muito difíceis de ler.

Não exagere. Lembre-se, o software é facilmente alterado. Em caso de dúvida, escreva nos dois sentidos e veja qual é mais fácil de ler.


11
Também é importante considerar a quantidade de dados que serão processados ​​através do lambda, pois os lambdas são bastante ineficientes em pequenas quantidades de processamento de dados. Eles realmente brilham na paralelização de grandes conjuntos de dados.
CraigR8806

11
@ CraigR8806 Não acho que o desempenho seja uma preocupação aqui. Usar uma função anônima ou criar uma classe estendendo a interface funcional deve ter o mesmo desempenho. As considerações de desempenho aparecem quando você fala sobre o uso da api de funções de ordem superior e streams sobre um loop de estilo imperativo, mas nesta pergunta estamos usando interfaces funcionais nos dois casos, apenas com sintaxe diferente.
puhlen

17
"embora seja bom repetir lambdas muito simples que são separadas" Somente se elas não mudarem necessariamente juntas. Se todas elas devem ter a mesma lógica para que seu código esteja correto, você deve torná-las todas realmente o mesmo método, para que as alterações precisem ser feitas apenas em um único local.
precisa saber é

No geral, esta é uma resposta muito boa. Mencionar o uso de uma referência de método como alternativa a um lambda consertaria o único furo que vejo nesta resposta.
Morgen

3
Em caso de dúvida, escreva nos dois sentidos e pergunte a alguém que mantém o código que é mais fácil de ler.
precisa saber é o seguinte

14

Depende. Sempre que você se encontrar usando o mesmo lambda em lugares diferentes, considere implementar uma classe que implemente a interface. Mas se você usasse uma classe interna anônima, caso contrário, acho que um lambda é muito melhor.


6
Não se esqueça da possibilidade de usar uma referência de método! A Oracle adicionou (e está adicionando ainda mais ao Java 9) métodos estáticos e métodos padrão em todo o lugar exatamente por esse motivo.
Jörg W Mittag

13

Apoio a resposta de Karl Bielefeldt, mas quero fornecer uma breve adição.

  • Depurando Alguns IDE lutam com o escopo dentro de um lambda e lutam para exibir variáveis ​​de membros dentro do contexto de um lambda. Embora, esperançosamente, essa situação mude na linha, pode ser irritante manter o código de outra pessoa quando estiver cheio de lambdas.

Definitivamente verdadeiro do .NET. Não me faz evitar lambdas, necessariamente, mas certamente não sinto nenhuma culpa se fizer outra coisa.
precisa saber é o seguinte

3
Se for esse o caso, você está usando o IDE errado. O IntelliJ e o Netbeans se saem muito bem nessa área em particular.
precisa

11
@ user1172763 No Visual Studio, se você deseja exibir variáveis ​​de membro, descobri que você pode subir a pilha de chamadas até chegar ao contexto da lambda.
Zev Spitz

6

Acesso a variáveis ​​locais do escopo anexo

A resposta aceita por Karl Bielefeldt está correta. Eu posso adicionar mais uma distinção:

  • Escopo

O código lambda aninhado dentro de um método dentro de uma classe pode acessar quaisquer variáveis efetivamente finais encontradas dentro desse método e classe.

Criar uma classe que implemente a interface funcional não fornece acesso direto ao estado do código de chamada.

Para citar o Tutorial Java (ênfase minha):

Como as classes locais e anônimas, as expressões lambda podem capturar variáveis; eles têm o mesmo acesso a variáveis ​​locais do escopo anexo . No entanto, diferentemente das classes local e anônima, as expressões lambda não apresentam problemas de sombreamento (consulte Shadowing para obter mais informações). Expressões lambda têm escopo lexicamente. Isso significa que eles não herdam nenhum nome de um supertipo ou introduzem um novo nível de escopo. As declarações em uma expressão lambda são interpretadas da mesma forma que no ambiente anexo.

Portanto, embora haja vantagens em extrair um código longo e nomeá-lo, você deve ponderá-lo contra a simplicidade do acesso direto ao estado do método e da classe anexos.

Vejo:


4

Isso pode ser muito difícil, mas para todos os outros pontos excelentes apresentados em outras respostas, eu acrescentaria:

Prefira as referências do método quando possível. Comparar:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

versus

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

Usar uma referência de método poupa a necessidade de nomear os argumentos do lambda, que geralmente são redundantes e / ou levam a nomes preguiçosos como eou x.


0

Com base na resposta de Matt McHenry, pode haver um relacionamento entre as referências lambda e o método em que o lambda pode ser reescrito como uma série de referências a métodos. Por exemplo:

employees.stream()
         .forEach(employeeName -> System.out.println(employee.getName()));

vs

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.