Objetos imutáveis em Java
1. Visão geral
Neste tutorial, aprenderemos o que torna um objeto imutável, como alcançar a imutabilidade em Java e quais as vantagens de fazer isso.
2. O que é um objeto imutável?
Um objeto imutável é umobject whose internal state remains constant after it has been entirely created.
Isso significa que a API pública de um objeto imutável nos garante que ele se comportará da mesma maneira durante toda a sua vida útil.
Se dermos uma olhada na classeString, podemos ver que mesmo quando sua API parece nos fornecer um comportamento mutável com seu métodoreplace, oString original não muda:
String name = "example";
String newName = name.replace("dung", "----");
assertEquals("example", name);
assertEquals("bael----", newName);
A API fornece métodos somente leitura, nunca deve incluir métodos que alteram o estado interno do objeto.
3. A palavra-chavefinal em Java
Antes de tentar alcançar a imutabilidade em Java, devemos falar sobre a palavra-chavefinal.
Em Java,variables are mutable by default, meaning we can change the value they hold.
Usando a palavra-chavefinal ao declarar uma variável, o compilador Java não nos deixará alterar o valor dessa variável. Em vez disso, ele relatará um erro em tempo de compilação:
final String name = "example";
name = "bael...";
Observe quefinal apenas nos proíbe de alterar a referência que a variável contém, não nos protege de alterar o estado interno do objeto ao qual se refere usando sua API pública:
final List strings = new ArrayList<>();
assertEquals(0, strings.size());
strings.add("example");
assertEquals(0, strings.size());
O segundoassertEquals falhará porque adicionar um elemento à lista altera seu tamanho, portanto, não é um objeto imutável.
4. Imutabilidade em Java
Agora que sabemos como evitar alterações no conteúdo de uma variável, podemos usá-la para criar a API de objetos imutáveis.
Construir a API de um objeto imutável exige que garantamos que seu estado interno não mudará, independentemente de como usarmos sua API.
Um passo adiante na direção certa é usarfinal ao declarar seus atributos:
class Money {
private final double amount;
private final Currency currency;
// ...
}
Observe que o Java nos garante que o valor deamount não mudará, esse é o caso com todas as variáveis de tipo primitivo.
No entanto, em nosso exemplo, temos apenas a garantia de quecurrency não mudará, entãowe must rely on the CurrencyAPI to protect itself from changes.
Na maioria das vezes, precisamos dos atributos de um objeto para armazenar valores personalizados, e o local para inicializar o estado interno de um objeto imutável é seu construtor:
class Money {
// ...
public Money(double amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
public Currency getCurrency() {
return currency;
}
public double getAmount() {
return amount;
}
}
Como dissemos antes, para atender aos requisitos de uma API imutável, nossa classeMoney tem apenas métodos somente leitura.
Usando a API de reflexão, podemos quebrar a imutabilidade echange immutable objects. No entanto, a reflexão viola a API pública do objeto imutável e, normalmente, devemos evitar fazer isso.
5. Benefícios
Como o estado interno de um objeto imutável permanece constante no tempo,we can share it safely among multiple threads.
Também podemos usá-lo livremente, e nenhum dos objetos que fazem referência a ele notará qualquer diferença, podemos dizer queimmutable objects are side-effects free.
6. Conclusão
Objetos imutáveis não mudam seu estado interno com o tempo, eles são thread-safe e sem efeitos colaterais. Devido a essas propriedades, objetos imutáveis também são especialmente úteis ao lidar com ambientes com vários threads.
Você pode encontrar os exemplos usados neste artigoover on GitHub.