Java 8: Lambda ou method reference? Entenda a diferença
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?
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 bytecode
s 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.