Funções Java - Entendendo o for-each loop
Seja no dia a dia de trabalho ou nos estudos, iremos nos deparar com alguma situação onde devemos iterar uma collection, como um Array ou um Map, ou seja, percorrer tal collection item a item. Em alguns casos, é possível utilizarmos o laço de repetição *for*, entretanto, a partir do Java 8, foi introduzida uma forma mais curta e elegante de iterar collections: o for-Each. Neste artigo, iremos abordar algumas formas de implementação do for-Each, entender um pouco mais sobre essa função do Java, utilizando como exemplo um sistema de disparo de e-mail fictício para uma lista de e-mails.
Entendendo o caso
Vamos supor que temos uma lista de e-mail de clientes da nossa plataforma, e desejamos enviar para eles um e-mail notificando-os sobre a promoção que ocorrerá no próximo final de semana. O método responsável pelo envio do e-mail é o sendEmail da nossa classe imaginária, portanto devemos percorrer a referida lista e enviar para cada elemento desta. Dessa forma, podemos utilizar o seguinte caso:
public class SendEmailExample {
// Método que fara a lógica ficcional de envio de email.
static String sendEmail(String email) {
return "Email enviado para: " + email;
}
public static void main(String[] args) {
var clientsEmails = new ArrayList<String>();
clientsEmails.add("cliente1@email.com");
clientsEmails.add("cliente2@email.com");
clientsEmails.add("cliente3@email.com");
}
Usando uma maneira mais simples, podemos percorrer essa lista com um for-loop da seguinte forma:
public class SendEmailExample {
// Método que fara a lógica ficcional de envio de email.
static String sendEmail(String email) {
return "Email enviado para: " + email;
}
public static void main(String[] args) {
var clientsEmails = new ArrayList<String>();
clientsEmails.add("cliente1@email.com");
clientsEmails.add("cliente2@email.com");
clientsEmails.add("cliente3@email.com");
for (int i = 0; i < clientsEmails.size(); i++) {
System.out.println(sendEmail(clientsEmails.get(i)));
}
}
}
Ou também utilizando o for-loop aprimorado, ficando dessa maneira:
public class SendEmailExample {
// Método que fara a lógica ficcional de envio de email.
static String sendEmail(String email) {
return "Email enviado para: " + email;
}
public static void main(String[] args) {
var clientsEmails = new ArrayList<String>();
clientsEmails.add("cliente1@email.com");
clientsEmails.add("cliente2@email.com");
clientsEmails.add("cliente3@email.com");
for (String email: clientsEmails) {
System.out.println(email);
}
}
}
E essa seria a saída, conforme esperado para ambos os casos:
Email enviado para: client1@email.com
Email enviado para: client2@email.com
Email enviado para: client3@email.com
Conhecendo o for-Each
Como mencionado anteriormente, a partir do Java 8, foi implementada a função for-Each para as collections. Assim tornando o nosso código menos verboso. Vejamos como ficaria o exemplo acima com o uso do for-each:
public class SendEmailExample {
// Método que fara a lógica ficcional de envio de email.
static String sendEmail(String email) {
return "Email enviado para: " + email;
}
public static void main(String[] args) {
var clientsEmails = new ArrayList<String>();
clientsEmails.add("cliente1@email.com");
clientsEmails.add("cliente2@email.com");
clientsEmails.add("cliente3@email.com");
clientsEmails.forEach(email -> System.out.println(sendEmail(email)));
}
}
A função for-Each é um método público da classe ArrayList, e acessando-o, podemos ver que, na sua implementação, é usado o paradigma funcional. Bom, mas então uma função é um método e método é uma função? Vou deixar um outro artigo em que esse tema é melhor explicado. Inspecionando o for-Each, vemos que ele faz algumas validações de tamanho da lista que será percorrida e voilá! Temos dois laços de repetição for por debaixo dos panos.
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
final Object[] es = elementData;
final int size = this.size;
for (int i = 0; modCount == expectedModCount && i < size; i++)
action.accept(elementAt(es, i));
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Entendendo a estrutura do for-Each
Vamos voltar a olhar o exemplo do envio de e-mail que utilizamos acima e entender melhor o for-Each loop:
clientsEmails.forEach(email -> System.out.println(sendEmail(email)));
Como o clientsEmails é um arraylist, conseguimos utilizar o loop através da notação ponto, e passamos o "e-mail" como o índice, ou seja, da mesma forma que passamos o string e-mail no for-loop aprimorado, porém utilizando-o como parâmetro. Logo após temos a lambda expression, que indica que um parâmetro retornará um valor, que no caso é o print do nosso método sendEmail. Como só temos um único método sendo executado, não há a necessidade de colocarmos chaves após o parâmetro, mas caso tivesse, poderíamos fazer desse jeito:
clientsEmails.forEach(email -> {
System.out.println("Enviando email para: "+email);
System.out.println(sendEmail(email));
});
Também podemos utilizar o for-Each loop com o method reference, deixando de forma mais
enxuta o nosso código.
clientsEmails.forEach(SendEmailExample::sendEmail);
No method reference, referenciamos que o loop irá iterar a lista, e para cada índice ele irá chamar o método sendEmail da nossa classe. Porém note que, diferentemente dos outros exemplos, não podemos adicionar outros métodos, assim como os prints dos exemplos acima.
Conclusão
Vimos que, com o for-Each loop, podemos iterar listas com um código bem mais enxuto, trazendo assim uma maior compreensão em nosso projetos. Deixarei alguns links abaixo de materiais para que você possar aprofundar cada vez mais em seus estudos. Espero ter ajudado!
Um guia sobre forEach (inglês) - https://www.baeldung.com/foreach-java
Funções vs Métodos - https://pt.stackoverflow.com/questions/212265/fun%c3%a7%c3%a3o-e-m%c3%a9todo-s%c3%a3o-a-mesma-coisa/212272#212272
Programação funcional com Java -https://www.devmedia.com.br/programacao-funcional-com-java/32176