Novidades do Java 8: Lambda Expressions

Em um post anterior, falamos sobre as novas funcionalidades introduzidas com a recente liberação da versão 8 do Java. E entre estas novidades, provavelmente aquela que trará maior impacto no dia-a-dia do desenvolvedor são as Lambda Expressions, sobre as quais falaremos neste post.

Amados por uns, odiados por outros, os Lambdas trazem um novo paradigma de desenvolvimento para o Java, inspirado em outras linguagens, como Javascript e C#. De forma geral, este paradigma permite tratar trechos de código como uma estrutura de dados. Sim, isso mesmo. Trechos de código podem ser passados como parâmetro para métodos, como se fossem objetos.

O uso clássico de Lambda Expressions simplifica bastante a utilização de classes anônimas. Um cenário bem típico é o tratamento de eventos em Swing, como no caso abaixo, em que tratamos o clique em um botão usando código escrito em Java 7:

JButton button = new JButton();
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Hello World!");
});
}

Veja que apenas a linha 5 é de fato relevante para determinar o comportamento do botão, mas ainda assim precisamos adicionar uma boa quantidade de código para fazer todo o tratamento.

Java 8 por sua vez permite simplificar bastante este código, usando a premissa de que ActionListener é uma interface que possui apenas um método:

public interface ActionListener extends EventListener {
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);
}

Interfaces com esta característica são conhecidas como functional interfaces e justamente por terem um método apenas, podem ser substituídas por lambdas. Em Java 8 este código ficaria desta forma:

button.addActionListener(e -> JOptionPane.showMessageDialog(null, "Hello World!"));

Bem mais simples, não? Note que na notação de lambdas, o parâmetro “e” acima é o mesmo parâmetro da interface ActionListener. E o trecho de código pode fazer referência ao mesmo. Claro, o uso de lambdas não se restringe a um comando apenas. O código acima pode ser escrito também desta forma: [code language=”java”]button.addActionListener(e -> { String mensagem = “Hello World”; JOptionPane.showMessageDialog(null, mensagem); } );

Vejam que agora estamos passando um bloco de código como parâmetro.

Cabe ao desenvolvedor ter senso crítico pra decidir caso a caso se é melhor usar a forma tradicional ou os lambdas, levando sempre em consideração a legibilidade de código.

O exemplo acima parece muito simples para justificar a introdução dos lambdas em Java, mas em conjunto com outras features, os mesmos tornam-se bastante poderosos. A API de Collections especialmente trouxe o conceito de Streams, que aliados aos lambdas permitem construções poderosas em poucas linhas.

Vamos imaginar que temos uma classe Funcionario, descrita abaixo:

public class Funcionario {

public static enum Sexo {
MASCULINO,
FEMININO;
}

private String nome;
private int idade;
private Double salario;
private Sexo sexo;

// getters e setters omitidos propositalmente
}

e que temos uma lista de funcionarios em mãos.

List<Funcionarios> funcionarios = funcionariosService.buscaFuncionarios();

Temos uma funcionalidade no sistema onde é necessário consultar o salário médio dos funcionários com uma idade mínima.

Até Java 7, faríamos algo desta forma:

private Double mediaSalarial(List<Funcionario> funcionarios, int idadeMinima) {

double soma = 0d;
int contagem = 0;

for (Funcionario f : funcionarios) {
if (f.getIdade() >= idadeMinima) {
soma = soma + f.getSalario();
contagem++;
}
}
// nao tratamos o caso de não haver nenhum funcionário por simplicidade de código

return soma / contagem;
}

Já em Java 8 podemos reduzir o código:

private Double mediaSalarial(List<Funcionario> funcionarios, int idadeMinima) {

Double media = funcionarios
.stream()
.filter(f -> f.getIdade() >= idadeMinima)
.mapToDouble(f -> f.getSalario())
.average()
.getAsDouble();

return media;
}

No caso acima, stream() é um novo método da API de Collections, que retorna um java.util.Stream<Funcionario>, classe que permite uma série de operações sobre a lista.
Neste exemplo, .filter() e .mapToDouble() são dois métodos que recebem uma functional interface como parâmetro e que portanto podem fazer uso de lambdas.

Uma diferença conceitual entre as duas abordagens é que com Streams estamos informando ao compilador o que o código deve fazer – calcular a média salarial – e não como deve fazer. A implementação de stream utilizada pode fazer a iteração de modo sequencial ou paralela, ou ainda usar recursos externos. Já no primeiro estamos dizendo explicitamente para o código iterar sequencialmente sobre cada elemento da Collection.

Veja também que o código evitou que tivéssemos que escrever código redundante. Afinal, para filtrar uma Collection, o que importa é a condição para selecionar ou não um item, não precisamos nos preocupar com a iteração.
De forma semelhante, o cálculo de uma média é sempre o mesmo, o que muda são os dados de entrada, que puderam ser facilmente obtidos de cada funcionário por mapToDouble.

Nestes casos, vemos sim Lambdas simplificando código, mas a recomendação do exemplo anterior também vale aqui: cabe a cada um avaliar se o código resultante do uso de lambdas é legível e de fácil manutenção, além de atender aos requisitos não funcionais do caso de uso.

Recomendamos que outros artigos sobre o assunto também sejam lidos, e que você experimente o uso de lambdas no seu projeto, para que possa decidir em que situações você considera seu uso pertinente.
A Oracle divulgou em seu site alguns vídeos introdutórios sobre o Java 8, que podem ajudá-lo a conhecer ainda mais esta nova estrutura da linguagem.

Em breve, outros posts falarão sobre mais novidades do Java 8. Fique atento!

Por LUIS SERGIO F. CARNEIRO

Postado em: 03 de abril de 2014

Confira outros artigos do nosso blog

REST não é JSON

21 de agosto de 2017

Bruno Sofiato

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

JavaScript 6: diferença entre var, let e const

09 de maio de 2017

Otávio Felipe do Prado

Deixe seu comentário