Java 8: Lambda ou method reference? Entenda a diferença

Java 8: Lambda ou method reference? Entenda a diferença
lucas.romao
lucas.romao

Compartilhe

Introdução

O Java 8 trouxe algumas novas funcionalidades e dentre elas a possibilidade de usarmos lambdas e method references. Caso ainda não esteja familiarizado com esses recursos, talvez se interesse pelo nosso post de o mínimo que você deve saber sobre Java 8. As duas features são relacionadas e nos ajudam a reduzir a quantidade de código escrito, com uma abordagem um pouco mais funcional.

Para dar um exemplo de uso de cada uma delas, vamos supor que temos uma lista de nomes e que gostaríamos de imprimir seu conteúdo. Antes do lambda e method reference, o código ficaria dessa forma:

 List<String> nomes = Arrays.asList(“Lucas”, “Rodrigo”, “Paulo”); for(String nome : nomes) { System.out.println(nome); } 

Que nos resultaria na saída:

 Lucas Rodrigo Paulo 

Esse mesmo código, com uso da expressão lambda e o método default forEach, poderia ficar assim, em uma única linha:

 nomes.forEach(nome -> System.out.println(nome)); 

E daria para fazer o mesmo com o method reference, deixando ainda mais simples:

 nomes.forEach(System.out::println); 

Em todos os casos teríamos a mesma saída:

 Lucas Rodrigo Paulo 

Mas qual a real diferença entre essas abordagens? Qual das duas versões você prefere usar no seu dia a dia?

Banner promocional da Alura, com um design futurista em tons de azul, apresentando dois blocos de texto, no qual o bloco esquerdo tem os dizeres:

Existe uma opção melhor para todos os casos?

Ao perceber que as features fazem coisas parecidas é normal se perguntar qual delas é a melhor. A resposta seria depende, nenhuma delas é a melhor solução para todos os casos. Entender a diferença entre elas é fundamental para saber quando é o melhor momento de usar cada uma.

Podemos usar method reference sempre?

É importante ressaltar que nem sempre é possível substituir um lambda por um method reference. Para conseguir fazer uma chamada a um method reference é necessário que a invocação de método da direita receba os mesmos parâmetros da esquerda do lambda.

No exemplo abaixo temos a nossa lista de nomes e queremos criar uma nova lista apenas com apenas as primeiras 3 letras de cada nome.

 List<String> nomes = Arrays.asList(“Lucas”, “Rodrigo”, “Alura”);

List<String> nomesReduzidos = nomes .map(nome -> nome.substring(0, 3)) .collect(Collectors.toList()); 

Nesse caso não seria possível usar o method reference, uma vez que para chamar o método substring seria necessário passar um parâmetro externo, os números 0 e 3, que definem o início e fim da nova string.

Vantagens de se usar Method Reference

Apesar de não poder ser usado em todos os casos onde se usa a lambda, method references possuem algumas vantagens bem legais. A primeira delas está no fato de usar menos símbolos, o que facilita bastante a legibilidade do código.

Há uma outra vantagem, que é a facilidade de se entender o que está sendo manipulado, uma vez que o tipo do objeto fica sempre muito explícito. Para exemplificarmos: suponha que temos uma classe Pessoa e essa classe possui o método getName, utilizando lambda para chamar esse método faríamos algo do tipo:

 p -> p.getName() 

Veja que é necessário entender qual o contexto para poder entender o que é o *p*, enquanto com method reference, teríamos algo como

 Pessoa::getName 

Fica bem mais fácil identificar o elemento que está sendo manipulado, percebe? Está explicito.

Outros usos do method reference

Um uso para o method reference que nem sempre é de conhecimento geral é que ele também funciona em métodos que recebem mais de um parâmetro, contanto que a quantidade seja a mesma que a de atributo do objeto que estamos usando.

Imagine que temos um mapa de alunos e sua nota, e gostaríamos de passar isso para um outro mapa.

Usando lambdas, nosso código seria:

 Map<String, Integer> novoMapaDeNotas = new HashMap<String, Integer>(); notasAlunos.forEach( (nome, nota) -> novoMapaDeNotas.put(nome, nota)); 

Veja que o lado esquerdo do lambda recebe dois parâmetros, o nome e a nota. O lado direito também recebe esses parâmetros, nessa mesma ordem. Que tal então fazer dessa forma, com method reference:

 Map<String, Integer> novoMapaDeNotas = new HashMap<String, Integer>(); notasAlunos.forEach(novoMapaDeNotas::put); 

E quanto a diferença de performance?

Pensando na questão de performance o lambda faz uma chamada de método a mais do que o method reference para executar o método, e as duas abordagens resultam em bytecodes diferentes, entretanto para a maioria dos casos essa diferença não chega a afetar performance. Só precisa ser levada em consideração em sistemas críticos onde a performance é essencial, não no forEach e manipulações simples do dia a dia.

E você, já tem usado Java 8 e esses recursos em seus projetos de produção? Aqui na Caelum e Alura usamos bastante. Tem até um curso dando uma visão bem legal sobre as novidades dessa versão.

Veja outros artigos sobre Programação