Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?
idcomp=32826
www.devmedia.com.br
[versão para impressão]
Link original: https://www.devmedia.com.br/como-usar-funcoes-lambda-em-java/32826
Como usar funções lambda em Java
Veja nesse artigo como utilizar as funções lambda em Java, recurso
disponível no Java 8.
Aprenda nesse artigo a utilizar as funções lambda em Java, conceito esse adicionado ao Java 8, e
que tem como principal objetivo adicionar ao Java técnicas de linguagens funcionais, como Scala e
LISP. A grande vantagem de funções lambda é diminuir a quantidade de código necessária para a
escrita de algumas funções, como por exemplo, as classes internas que são necessárias em
diversas partes da linguagem Java, como Listeners e Threads.
Simplificando um pouco a definição, uma função lambda é uma função sem declaração, isto é, não
é necessário colocar um nome, um tipo de retorno e o modificador de acesso. A ideia é que o
método seja declarado no mesmo lugar em que será usado. As funções lambda em Java tem a
sintaxe definida como (argumento) -> (corpo), como mostram alguns exemplos da Listagem 1.
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
a -> a > 10
Listagem 1. Exemplos de funções lambda em Java
Uma função lambda pode ter nenhum ou vários parâmetros e seus tipos podem ser colocados ou
podem ser omitidos, dessa forma, eles são inferidos pelo Java (veremos alguns exemplos disso
mais para frente). A função lambda pode ter nenhum ou vários comandos: se a mesma tiver apenas
um comando as chaves, não são obrigatórias e a função retorna o valor calculado na expressão; se
a função tiver vários comandos, é necessário colocar as chaves e também o comando return - se
nada for retornado, a função tem um retorno void.
Nota: Já viu o Checklist Programador Java que a DevMedia preparou para todos?
Para demonstrar como utilizar as funções lambada em Java, vamos analisar alguns casos. O
primeiro exemplo será a utilização com threads, onde elas são muito utilizadas para simplificar o
código. O segundo exemplo será a utilização com as classes de coleção do Java, para aumentar a
flexibilidade de diversas funções, como ordenar e filtrar listas. O terceiro exemplo será a utilização
com classes do tipo Listeners, onde as funções lambda são utilizadas para simplificar o código. E
1 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
finalmente, o quarto exemplo será para a criação de funções genéricas, que aceitam expressões
lambda como parâmetros, e podem ser utilizadas em diversos problemas.
Exemplo 1 – Funções Lambda com Threads
Para exemplificar a utilização de expressões lambda com threads será analisado um programa que
cria uma thread com uma função interna e que vai apenas mostrar a mensagem “Thread com
classe interna!”. A Listagem 2 mostra o código dessa implementação.
Runnable r = new Runnable() {
public void run() {
System.out.println("Thread com classer interna!");
}
};
new Thread(r).start();
Listagem 2. Thread criado com uma classe interna
Primeiro é criada uma implementação do método run da interface Runnable, e em seguida é criada
a Thread com essa implementação. É possível verificar a grande quantidade de código necessário
para um exemplo bastante simples.
Já com a utilização de expressões lambda, o código necessário para a implementação dessa
mesma funcionalidade é bastante simples e bem menor que o anterior. A Listagem 3 mostra um
exemplo do código da expressão lambda com threads.
Runnable r = () -> System.out.println("Thread com função lambda!");
new Thread(r).start();
Listagem 3. Thread com funções lambda
Essa expressão não passa nenhum parâmetro, pois ela será passada para a função run, definida
na interface Runnable, que não tem nenhum parâmetro, então ela também não tem nenhum
retorno.
Um código ainda mais simples é a passagem da função diretamente como parâmetro para o
construtor da classe Thread. A Listagem 4 mostra um exemplo desse código, mostrando que as
funções lambda podem ser definidas e passadas como parâmetros diretamente para outros
métodos, e isso pode ser bastante útil, como veremos nos próximos exemplos.
new Thread(
() -> System.out.println("hello world")
).start();
Listagem 4. Função lambda passada como parâmetro para um método
Exemplo 2 – Funções Lambda com as classes de Collections
2 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
As funções lambdas podem ser bastante utilizadas com as classes de coleções do Java, pois
nessas fazemos diversos tipos de funções que consistem basicamente em percorrer a coleção e
fazer uma determinada ação, como por exemplo, imprimir todos os elementos da coleção, filtrar
elementos da lista e buscar um determinado valor na lista.
A Listagem 5 mostra um exemplo de como normalmente é feito o código para percorrer uma lista e
imprimir os valores dentro dela.
System.out.println("Imprime todos os elementos da lista!");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
for(Integer n: list) {
System.out.println(n);
}
Listagem 5. Imprimindo os elementos de uma lista
Com as funções lambda é possível implementar a mesma funcionalidade com muito menos código,
bastando chamar o método forEach de uma lista, que é um método que espera uma função lambda
como parâmetro. Esse método executará, a cada iteração na lista, a função passada como
parâmetro. A Listagem 6 mostra o exemplo de imprimir todos os elementos de uma lista com
expressão lambda.
System.out.println("Imprime todos os elementos da lista!");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
Listagem 6. Imprimindo os elementos de uma lista com expressões lambda
Dentro do código de uma função lambda é possível executar diversos comandos, como por
exemplo, na Listagem 7, que antes de imprimir o número, verifica se ele é par ou ímpar: se for par
o numero é impresso, caso contrário, nada é realizado. Nesse exemplo é possível verificar que
dentro de uma expressão lambda pode ser realizado qualquer tipo de operação.
System.out.println("Imprime todos os elementos pares da lista!");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> {
if (n % 2 == 0) {
System.out.println(n);
}
});
Listagem 7. Imprimindo apenas os elementos pares de uma lista
Mais um exemplo do que pode ser feito com funções lambda, são expressões matemáticas. Na
Listagem 8 é exibido o código para mostrar o quadrado de todos os elementos de uma lista de
números inteiros.
System.out.println("Imprime o quadrado de todos os elementos da lista!");
3 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n * n));
Listagem 8. Imprimindo o quadrado dos elementos da lista
Funções lambda podem ser utilizadas também para a ordenação de listas com a interface
Comparator. Por exemplo, caso exista uma classe Pessoa com os atributos nome e idade e é
necessário ordenar uma lista em ordem alfabética pelo nome, ou em ordem das idades, é
necessário implementar dois comparators, um para cada tipo de parâmetro, e chamá-lo no método
sort da lista que será ordenada. A Listagem 9 mostra o código do exemplo descrito utilizando a
interface Comparator tradicional. O código da classe Pessoa foi omitido, mas ela é uma classe
bastante simples, apenas com os atributos nome e idade, o construtor e os métodos get e set.
System.out.println("Ordenando pessoas pelo nome:");
List<Pessoa> listPessoas = Arrays.asList(new Pessoa("Eduardo", 29),
new Pessoa("Luiz", 32), new Pessoa("Bruna", 26));
Collections.sort(listPessoas, new Comparator<Pessoa>() {
@Override
public int compare(Pessoa pessoa1, Pessoa pessoa2){
return pessoa1.getNome().compareTo(pessoa2.getNome());
}
});
listPessoas.forEach(p -> System.out.println(p.getNome()));
System.out.println("Ordenando pessoas pela idade:");
Collections.sort(listPessoas, new Comparator<Pessoa>() {
@Override
public int compare(Pessoa pessoa1, Pessoa pessoa2){
return pessoa1.getIdade().compareTo(pessoa2.getIdade());
}
});
listPessoas.forEach(p -> System.out.println(p.getNome()));
Listagem 9. Ordenando objetos de uma classe pessoa utilizando comparators
É fácil observar que o código, apesar de bastante simples, ficou muito grande, apenas para ordenar
duas vezes a lista com dois parâmetros diferentes.
É possível reimplementar esse exemplo utilizando funções lambda e deixando o código muito mais
conciso. A Listagem 10 mostra a reimplementarão da Listagem 9, mas agora utilizando
expressões lambda.
List<Pessoa> listPessoas = Arrays.asList(new Pessoa("Eduardo", 29),
new Pessoa("Luiz", 32), new Pessoa("Bruna", 26));
System.out.println("Ordenando pessoas pelo nome:");
Collections.sort(listPessoas, (Pessoa pessoa1, Pessoa pessoa2)
-> pessoa1.getNome().compareTo(pessoa2.getNome()));
4 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
listPessoas.forEach(p -> System.out.println(p.getNome()));
System.out.println("Ordenando pessoas pela idade:");
Collections.sort(listPessoas, (Pessoa pessoa1, Pessoa pessoa2)
-> pessoa1.getIdade().compareTo(pessoa2.getIdade()));
listPessoas.forEach(p -> System.out.println(p.getNome()));
Listagem 10. Comparator com expressões lambda
Vejam que os dois exemplos implementam exatamente a mesma funcionalidade, mas o código
utilizando expressões lambda tem apenas cinco linhas, enquanto que o código que não utiliza
expressões lambda tem 15 linhas.
Com expressões lambda também é possível filtrar elementos de uma coleção de objetos criando
para isso um stream de dados (também um novo conceito do Java 8). É chamado o método filter do
stream e como parâmetro para esse método é passado uma função lambda que faz o filtro dos
elementos desejados. A Listagem 11 mostra dois exemplos de filtros sendo realizados também na
listagem de pessoas utilizadas nos exemplos anteriores. O primeiro filtro é feito apenas para
pessoas com mais de 30 anos e o segundo para apenas pessoas que tem o nome iniciado com a
letra “E”.
System.out.println("Pessoas com mais de 30 anos:");
List<Pessoa> maioresTrinta = listPessoas.stream().filter(p
-> p.getIdade() > 30).collect(Collectors.toList());
maioresTrinta.forEach(p -> System.out.println(p.getNome()));
System.out.println("Pessoas que o nome iniciam com E:");
List<Pessoa> nomesIniciadosE = listPessoas.stream().filter(p
-> p.getNome().startsWith("E")).collect(Collectors.toList());
nomesIniciadosE.forEach(p -> System.out.println(p.getNome()));
Listagem 11. Filtros com Funções Lambda
Exemplo 3 – Funções Lambda utilizadas com Listeners
Listeners são classes que implementam o padrão de projeto Observer, que representa objetos que
ficam esperando ações realizadas em outros objetos e, a partir dessa ação, executam algum
código. Um exemplo bem comum são os Listeners de botões da API de interfaces gráficas Swing.
Por exemplo, quando é necessário implementar um código para realizar alguma ação quando um
usuário clica em um JButton, passamos um objeto do tipo ActionListener para o método
addActionListener do botão e isso, normalmente, é implementado com classes internas. A
Listagem 12 exibe o código para a criação desse listener.
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("O botão foi pressionado!");
//Realiza alguma ação quando o botão for pressionado
5 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
}
});
Listagem 12. Listener sem utilizar funções lambda
Apesar de funcionar, o código exibido é muito grande, mas utilizando funções lambda é possível
implementar a mesma funcionalidade com um código muito mais enxuto e simples de entender. A
Listagem 13 mostra como esse mesmo código seria implementado utilizando funções lambda.
button.addActionListener( (e) -> {
System.out.println("O botão foi pressionado, e o código
executado utiliza uma função lambda!");
//Realiza alguma ação quando o botão for pressionado
});
Listagem 13. Listener utilizando funções lambda
Praticamente qualquer Listener pode ser escrito utilizando expressões lambda, como os para
escrever arquivos em logs e para verificar se um atributo foi adicionado em uma sessão de um
usuário em aplicação web.
Exemplos 4 – métodos que aceitam funções lambda como parâmetros
Além de escrever funções lambda, também é possível criar métodos que as recebam como
parâmetro, o que é bastante útil e pode tornar um método bastante flexível. Por exemplo, podemos
criar um método genérico para imprimir elementos de uma lista, mas passamos como parâmetro a
função para a filtragem dos elementos dessa lista, assim, com apenas um método, e passando a
função como parâmetro, é possível fazer a filtragem da lista de várias maneiras diferentes. A
Listagem 14 mostra um exemplo de como poderia ser feito isso.
package teste.devmedia;
import java.util.Arrays;
import java.util.List;
import java.util.function.IntFunction;
import java.util.function.Predicate;
public class Main {
public static void main(String [] a) {
System.out.println("Cria a lista com os elementos que serão realizadas operações");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
System.out.println("Imprime todos os números:");
avaliaExpressao(list, (n)->true);
6 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
System.out.println("Não imprime nenhum número:");
avaliaExpressao(list, (n)->false);
System.out.println("Imprime apenas número pares:");
avaliaExpressao(list, (n)-> n%2 == 0 );
System.out.println("Imprime apenas números impares:");
avaliaExpressao(list, (n)-> n%2 == 1 );
System.out.println("Imprime apenas números maiores que 5:");
avaliaExpressao(list, (n)-> n > 5 );
System.out.println("Imprime apenas números maiores que 5 e menores que 10:");
avaliaExpressao(list, (n)-> n > 5 && n < 10);
public static void avaliaExpressao(List<Integer> list, Predicate<Integer> predicate) {
list.forEach(n -> {
if(predicate.test(n)) {
System.out.println(n + " ");
}
});
}
Listagem 14. Método para a filtragem de elementos de uma lista
O método avaliaExpressao recebe como parâmetro uma lista e um objeto do tipo Predicate, que é
uma interface, que espera uma função logica, isto é, que avalia uma expressão booleana, e retorna
true ou false. Essa função é executada chamando o método test, que executará a função passada
como parâmetro. Ao ser avaliada, se essa função retornar verdadeiro, imprime o valor da lista, caso
contrário, não imprime nada.
A Listagem 15 exibe um código parecido, mas ao invés de um Predicate, a função passada como
parâmetro para o método é um IntFunction, que espera funções que são realizadas sobre números
inteiros, como soma e multiplicação. No exemplo, é possível observar que dentro do método
realizaOperacao, o objeto function chama o método apply, que executa a função lambda passada
como parâmetro e, assim como o método anterior, permite muita flexibilidade. Podemos fazer
qualquer tipo de operação sobre uma lista de números inteiros.
package teste.devmedia;
import java.util.Arrays;
import java.util.List;
7 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
import java.util.function.IntFunction;
import java.util.function.Predicate;
public class Main {
public static void main(String [] a) {
System.out.println("Cria a lista com os elementos que serão realizadas operações");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
System.out.println("Multiplica todos os elementos da lista por 5:");
realizaOperacao(list, (n)-> n * 5);
System.out.println("Calcula o quadrado de todos os elementos da lista:");
realizaOperacao(list, (n)-> n * n);
System.out.println("Soma 3 em todos os elementos da lista:");
realizaOperacao(list, (n)-> n + 3);
System.out.println("Coloca 0 em todos os elementos da lista:");
realizaOperacao(list, (n)-> 0);
public static void realizaOperacao(List<Integer> list, IntFunction<Integer> function) {
list.forEach(n -> {
n = function.apply(n);
System.out.println(n + " ");
});
}
Listagem 15. Método para realizar operações matemáticas em uma lista
Assim como o Predicate e o IntFunction, existem diversas outras interfaces que podem ser
utilizadas para utilizar funções lambada. Todas elas têm o mesmo objetivo, que é receber uma
função como parâmetro, mas a diferença entre essas interfaces são o tipo e o número de
parâmetros de entrada esperados e o tipo de retorno da função.
As funções lambda são mecanismos bastante poderosos, que facilitam muito a escrita de código
conciso e evitam que o programador seja obrigado a escrever um monte de código “inútil”,
principalmente em operações simples, além de flexibilizar o mesmo.
Apesar de serem bastante úteis, as funções lambda nem sempre são a melhor opção, caso seja
necessário reutilizar diversas vezes uma função, talvez seja melhor criar uma classe ou interface
8 of 9 05/09/2019 09:17
Como usar funções lambda em Java https://www.devmedia.com.br/view/print.php?idcomp=32826
com apenas um método e não uma expressão lambda.
Espero que esse artigo tenha sido útil, até a próxima!
Links:
Descrição do Padrão de Projeto Observer
Interfaces para utilizar funções lambda
Documentação oficial da Oracle sobre funções lambdas
Referência da Oracle para funções lambda
9 of 9 05/09/2019 09:17