Expressão Lambda - Parte II (Quando e como usar)
Tags: todayilearned, java, braziliandevs, tutorial
Published: 2021-11-04 23:16:29

Parte 02 de 03 da documentação Oracle sobre **Expressão Lambda** :)

Um problema com classes anônimas é que, se sua implementação for muito simples (sendo uma interface que contém um único método, por exemplo), a sua sintaxe pode parecer incerta e não muito clara.

Nesses casos, normalmente você está tentando passar funcionalidade como argumento para outro método, semelhante a iniciar uma ação quando alguém clica num botão.

As **expressões lambdas** te permitem fazer isso, trabalhar uma funcionalidade como argumento ou código como dado de informação.

No [artigo anterior sobre Classe Anônima](https://dev.to/diariodeumacdf/expressao-lambda-parte-i-classes-anonimas-1adc), mostramos como implementar uma classe sem a necessidade de se criar uma classe com um único método e implementar/estender outra classe para seguir com a construção da sua aplicação.

A **expressão Lambda** te permite fazer a mesma coisa, porém de uma forma mais conscisa ainda.

## Caso de Uso Ideal para Expressão Lambda

Vamos supor que você está criando uma aplicação de rede social. Você quer criar uma feature que permite o administrador a realizar determinada ação, como, por exemplo, enviar uma mensagem para membros que satisfaçam determinada condição.

![UML](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9yj42zkryicyc8ylus1.png)

Cada pessoa nessa rede social é representada pela classe Pessoa:

```java
public class Pessoa {
	public enum GENERO{
		MASCULINO, FEMININO
	}
	String nome;
	LocalDate nascimento;
	GENERO genero;
	String email;
	public int getIdade(){//...}	
	public void printPessoa(){//...}
	
}
```

Suponha que os membros dessa rede social estão armazenado numa `List<Pessoa>`

## Passo 01: Criar métodos que encontrem membros que atendem a uma certa característica

Uma forma muito simples de fazer essa filtragem é criar um método para cada tipo de filtro que se precisa fazer, como para gênero ou idade.

Exemplo:

```java
public static void printPessoasMaioresQue(List<Pessoa> relacao, int idade){
	for(Pessoa p : relacao){
		if(p.getIdade() >= age){
			p.printPessoa();
		}
	}
}
```

Esse método é muito frágil por conta da quantidade de atualizações que a aplicação ainda passará por conta de inclusão de novos membros.

Outro ponto, suponha que você atualizou a sua aplicação e alterou a estrutura da classe Pessoa que passa a conter mais variáveis, talvez isso impacte em todos os lugares em que você instanciou a classe de forma que o algoritmo que usa essa informação agora haja de forma diferente.

E, por fim, é um método restritivo, e se você precisar mostrar em tela usuários mais novo?

## Passo 02: Criar métodos de busca mais generalizados

O método abaixo é mais generalizado do que o anterior:

```java
public static void printPessoasComIdadeEntre(List<Pessoa> relacao, int menor, int maior){
	for(Pessoa p : relacao){
		if(menor <= p.getIdade() && p.getIdade() >= maior){
			p.printPessoa();
		}
	}
}
```

Mas e se você quiser mostrar em tela membros de determinado gênero ou a combinação de idade e gênero?

Ou se você quiser incluir atributos como status de relacionamento ou localização?

Apesar do método acima ser mais genérico do que o anterior, ter que criar um método para cada possibilidade de busca ainda deixa o código frágil.

Ao invés disso você pode separar as especificações de critério de busca do que você quer busca em classes separadas.

## Passo 03: Especificar o código de critério de busca em uma classe local

O método abaixo mostra em tela membros que atendem determinado critério de busca que você especificar:

```java
public static void printPessoas(List<Perssoa> relacao, VerificaPessoa criterio){
	for(Pessoa p: relacao){
		if(criterio.buscaPessoa(p)){
			p.printPessoa();
		}
	}
}
```

Esse método verifica cada Pessoa que consta na List relacao, se a pessoa atender o critério passado em `criterio.buscaPessoa(p)` então o método mostra em tela a pessoa.

Para especificar o critério de busca, você implementa uma interface chamada `VerificaPessoa`

```java
interface VerificaPessoa{
	boolean buscaPessoa(Pessoa p);
}
```

A classe que fará uso desse método, faz a implementação da interface, exemplo:

```java
class VerificaPessoaElegivelParaExercito implements VerificaPessoa{
	public boolean buscaPessoa(Pessoa p){
		return p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25;
	}
}
```

Para usar esse classe, você criar uma instância e invoca o printPessoa:

```java
printPessoa(relacao, new VerificaPessoaElegivelParaExercito())
```

Apesar dessa estrutura ser menos frágil, você não precisa reescrever métodos se você alterar a classe Pessoa, você ainda tem código adicional: uma **interface** nova e uma **classe local** para cada busca que você pretende fazer na sua aplicação.

## Passo 04: Especificar o critério de busca numa classe anônima

```java
printPessoa(relacao, new VerificaPessoa(){
	public boolean buscaPessoa(Pessoa p){
		return p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25;
	}
})
```

Essa forma reduz a quantidade de linhas de código porque você não precisa criar uma nova classe cada busca que você precisa realizar.

Entretanto, a sintaxe de uma classe anônima é volumoso considerando que a interface VerificaPessoa só contém um método. Nesse caso, você pode usar uma expressão lambda ao invés da classe anônima.

## Passo 05: Especificar o critério de busca numa expressão lambda

A interface VerificaPessoa é uma **interface funcional**. Uma classe funcional é aquela que contém apenas um método abstrato.

Exatamente por ela ser uma classe funcional que contém um único método abstrato, você pode omitir o nome do método quando você a implementa. Ao fazer isso, você estará implementando uma expressão lambda ao invés de uma classe anônima.

```java
printPessoa(relacao,(Pessoa p)-> p.genero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.get() <= 25);
```

Você ainda pode usar uma interface funcional padrão no lugar de criar uma interface, o que ajuda a reduzir a criação de código.

```java

interface Predicate<Pessoa>{
	boolean buscaPessoa(Pessoa p);	
}

public static void printPessoaComPredicate(List<Pessoa> relacao, Predicate<Pessoa> criterio){
	for(Pessoa p : relacao){
		if(criterio.buscaPessoa(p){
			p.printPessoa();
		}
	}
}

printPessoaComPredicate(relacao, p -> p.getGenero == Pessoa.Genero.MASCULINO && p.getIdade() >= 18 && p.getIdade() <= 25);
```

Fonte: [Documentação Oracle](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)
Colaboração: [Silvair Soares](https://dev.to/silvairsoares)