Boas práticas ao usar Lambda e classe anônima em Java
Tags: java, braziliandevs, tutorial, programming
Published: 2022-02-24 18:58:54

Já faz algum tempo que venho falando sobre classes locais, classes anônimas e expressões lambdas. O que são, como usar e quando usar. No atual post vou escrever sobre boas práticas ao usar lambda em Java. 

## Devo usar tudo o que a linguagem tem?

Acho relevante colocar aqui que apesar do Java ter muitos recursos que podem e realmente ajudam no dia a dia de uma pessoa desenvolvedora, não quer dizer exatamente que esses recursos ajudem a manter um controle e facilidade de leitura/alteração no momento da manutenção do código.

Portanto, quando falamos em “boas práticas” é focado no momento em que a aplicação foi para produção e, dali pra frente, teremos as manutenções, revisões, novas releases e etc. Do meu ponto de vista, sempre temos que pensar que a manutenção não necessariamente será realizada por outra pessoa, mas sim que pode ser qualquer pessoa, incluindo quem criou a aplicação, portanto, é importante pensarmos: 

> Qual a melhor forma de deixar esse código simples não só para alterações, mas de leitura e entendimento futuro?

Claro que não podemos da performance, segurança, arquitetura e etc, mas sempre mantendo em mente a questão de carga cognitiva de compreensão do código. Afinal, ninguém quer passar uma semana só para tentar entender  o que se passa diante dos seus olhos ali na IDE ou então ficar debugando linha a linha para tentar entender o que aquele trecho de código faz ou deixa de fazer.

Vai por mim, já tive que fazer isso porque dei de cara com uma aplicação sem o mínimo de documentação e muito menos com código legível e compreensível. Só para dar uma noção, conseguimos achar um dos responsáveis pela a aplicação e nem ele sabia explicar o que ele e/ou o time tinham feito, então... acho que deu para entender, né?

## E como faz?

Pensando nisso, tenho lido muito sobre design pattern (ou, em português, padrões de projeto) tanto da Gang of Four - GoF (ou, em português, Gangue dos Quatro), como o SOLID e, o mais recente nas minhas leituras, o Effective Java (Java Efetivo) do Joshua Bloch.

Esse último, até onde encontrei, está em sua 3ª edição e fala das melhores práticas em Java, usando o Java 9. Questionei no Twitter sobre a relação versão atual do Java (estamos na 18) versus a edição desse livro e a galera mais sênior em relação a mim, super recomendaram a leitura e adicionaram que, para o universo Java, é um dos livros que mais ajudam a entender como otimizar o código, simplificá-lo, além de dar uma visão mais ampla do próprio Java.

Dessa forma, o que Bloch fala sobre Lambdas é, basicamente o seguinte:

> Use expressão lambda ao inveś de classe anônima

Agora vamos ao porquê: Classe anônima é mais verbosa, ao passo que a expressão lambda é semelhante a função dentro da classe anônima, porém bem mais concisa.

Veja abaixo:

```java
//Exemplo usando classe anônima
1 Collections.sort(palavras, new Comparator<String>() {
2	 public int compare(String palavra1, String palavra2) {
3		 return Integer.compare(palavra1.length(), palavra2.length());
4	 }
5 });

//Exemplo usando expressão lambda
6 Collections.sort(palavras,
7 (palavra1, palavra2) -> Integer.compare(palavra1.length(), palavra2.length()));
```

Na expressão lambda e em grande maioria das vezes, o compilador será capaz de deduzir os tipos especificados na linha 2 da classe anônima por um processo chamado por inteferência de tipo, portanto, podemos reduzir ainda mais o exemplo de expressão lambda:

```java
//Exemplo usando classe anônima
1 Collections.sort(palavras, new Comparator<String>() {
2	 public int compare(String palavra1, String palavra2) {
3		 return Integer.compare(palavra1.length(), palavra2.length());
4	 }
5 });

//Exemplo 1 usando expressão lambda
6 Collections.sort(palavras,
7 (palavra1, palavra2) -> Integer.compare(palavra1.length(), palavra2.length()));

//Exemplo 2 usando expressão lambda em sua versão mais concisa
8 palavras.sort(comparingInt(String::length));
```

Outra dica muito importante que o Bloch passa ainda sobre essa questão de lambdas é de sempre que possível priorizar o uso de métodos genéricos, pois assim diminuirá as chances do compilador dar erro por inferência de tipo.

Por exemplo, as enums facilitam muito o uso interfaces funcionais e lambdas exatamente pelo conceito de métodos mais genéricos.

```java
//Exemplo de uso com Enum sem interface funcional
1 public enum Operacao {
2	SOMA("+") {
3		public double apply(double x, double y) { return x + y; }
4	},
5	SUBTRACAO("-") {
6		public double apply(double x, double y) { return x - y; }
7	},
8	MULTIPLICACAO("*") {
9		public double apply(double x, double y) { return x * y; }
10	},
11	DIVISAO("/") {
12		public double apply(double x, double y) { return x / y; }
13	};

14	private final String simbolo;

//Construtor da enum
15 Operacao(String simbolo) { 
16	this.simbolo = simbolo; 
17 }

//Pelas boas práticas de Bloch, sempre dar o override no toString
18 @Override public String toString() { 
19	return simbolo; 
20 }

//método abstrato para cada implementação
21 public abstract double apply(double x, double y);

22 }
```

Do jeito que o código está acima, para cada constante será dado um override no método `apply` afinal, cada operação precisa de uma ação diferente para o mesmo método.

Agora veja como isso fica mais conciso, ou seja, reduzido e simples ao usarmos lambda:

```java
1 public enum Operacao {
2	SOMA("+", (x, y) -> x + y),
3	SUBTRACAO("-", (x, y) -> x - y),
4	MULTIPLICACAO("*", (x, y) -> x * y),
5	DIVISAO("/", (x, y) -> x / y);

6 private final String simbolo;

7 private final DoubleBinaryOperator operacao;

//construtor da enum
8 Operation(String simbolo, DoubleBinaryOperator operacao) {
9		this.simbolo = simbolo;
10		this.operacao = operacao;
11 }

12 @Override public String toString() { 
13	return simbolo; 
14 }

/**o que antes era um método abstrato se torna um método concreto e 
* passamos a utilizar a interface funcional DoubleBinaryOperator cuja a ação é
* receber dois dados e receber um.
*/
15	public double apply(double x, double y) {
16		return operacao.applyAsDouble(x, y);
17	}
18 }
```

## Então vamos esquecer as classes abstrastas e classes anônimas?

Admito que o uso de classe abstrata ainda é algo um pouco confuso para mim se você for pensando em SOLID, por exemplo e até mesmo no próprio Java Efetivo, porém, gosto do ponto que o Bloch trás em seu livro quando diz o seguinte:

“[...] Lambdas are limited to functional interfaces. If you want to create an instance of an abstract class, you can do it with an anonymous class, but not a lambda. Similarly, you can use anonymous classes to create instances of interfaces with multiple abstract methods.”

Que, em tradução livre, nas mais é do que o uso de lambdas se restrigem ao uso de interfaces funcionais (algo que a própria [documentação do Java](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach6) reforça) e diz que a classe abstrata você utiliza numa classe anônima e NÃO em um lambda. Da mesma forma que você utiliza a classe anônima para criar instâncias de interfaces com múltiplos métodos.

Fontes: [Documentação Java](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach6)

[Effective Java - Joshua Bloch](https://www.amazon.com.br/Effective-Java-3rd-Joshua-Bloch/dp/0134685997/ref=asc_df_0134685997/?tag=googleshopp00-20&linkCode=df0&hvadid=379735814613&hvpos=&hvnetw=g&hvrand=18280191810329920088&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1001773&hvtargid=pla-361252606493&psc=1)