logo

Como criar uma classe imutável em Java?

Em Java, imutabilidade significa que, uma vez criado um objeto, seu estado interno não pode ser alterado. Classes imutáveis ​​​​em Java oferecem muitas vantagens, como segurança de thread, depuração fácil e tudo mais. Em Java todos os classes de wrapper (como Integer Boolean Byte Short) e a classe String é imutável. Também podemos criar nossa própria classe imutável.

Dhanashree Verma

Neste artigo vamos aprender:



  • O que significa imutabilidade
  • Por que é útil
  • Como criar nossa própria classe imutável
  • Por que a cópia profunda é importante
  • Quais são as limitações que os tipos de registro Java têm

O que é uma classe imutável?

Uma classe imutável é uma classe cujos objetos não podem ser alterados depois de criados. Se fizermos alguma modificação, isso resultará em um novo objeto. Este método é usado em aplicativos simultâneos.

Regras para criar uma classe imutável

  • A classe deve ser declarada como final para que as classes filhas não possam ser criadas.
  • Os membros de dados da classe devem ser declarados privado para que o acesso direto não seja permitido.
  • Os membros de dados da classe devem ser declarados como final para que não possamos alterar seu valor após a criação do objeto.
  • Um construtor parametrizado deve inicializar todos os campos executando um cópia profunda para que os membros de dados não possam ser modificados com uma referência de objeto.
  • A cópia profunda de objetos deve ser executada nos métodos getter para retornar uma cópia em vez de retornar a referência real do objeto.

Observação : Não deve haver setters ou, em termos mais simples, não deve haver opção para alterar o valor da variável de instância.




Exemplo: implementação de classe imutável

Aluno.java

Java
// Java Program to Create An Immutable Class import java.util.HashMap; import java.util.Map; // declare the class as final final class Student {  // make fields private and final  private final String name;  private final int regNo;  private final Map<String String> metadata;  // initialize all fields via constructor  public Student(String name int regNo Map<String String> metadata) {  this.name = name;  this.regNo = regNo;  // deep copy of mutable object (Map)  Map<String String> tempMap = new HashMap<>();  for (Map.Entry<String String> entry : metadata.entrySet()) {  tempMap.put(entry.getKey() entry.getValue());  }  this.metadata = tempMap;  }  // only provide getters (no setters)  public String getName() {  return name;  }  public int getRegNo() {  return regNo;  }  // return deep copy to avoid exposing internal state  public Map<String String> getMetadata() {  Map<String String> tempMap = new HashMap<>();  for (Map.Entry<String String> entry : this.metadata.entrySet()) {  tempMap.put(entry.getKey() entry.getValue());  }  return tempMap;  } } 

Neste exemplo, criamos uma classe final chamada Estudante. Possui três membros de dados finais, um construtor parametrizado e métodos getter. Observe que não há método setter aqui. Observe também que não precisamos realizar cópia profunda ou clonagem de membros de dados de tipos de wrapper, pois eles já são imutáveis.



Geeks.java:

Java
import java.util.HashMap; import java.util.Map; public class Geeks {  public static void main(String[] args) {  // create a map and adding data  Map<String String> map = new HashMap<>();  map.put('1' 'first');  map.put('2' 'second');  // create an immutable Student object  Student s = new Student('GFG' 101 map);  // accessing data  System.out.println(s.getName());   System.out.println(s.getRegNo());   System.out.println(s.getMetadata());   // try to modify the original map  map.put('3' 'third');  System.out.println(s.getMetadata());   // try to modify the map returned by getMetadata()  s.getMetadata().put('4' 'fourth');  System.out.println(s.getMetadata());   } } 

Mesmo depois de modificar o Mapa original ou retornado, o estado interno do objeto Aluno permanece inalterado. Isso confirma o conceito de imutabilidade.

Saída:

GFG  
101
{1=first 2=second}
{1=first 2=second}
{1=first 2=second}


Limitação de registro Java com campos mutáveis

Java 14 introduzido registro . Esta é uma maneira clara e concisa de definir classes semelhantes imutáveis:

gravar Aluno(Nome da string int regNo Mapametadados) {}


Mas isso oferece apenas uma imutabilidade superficial. Se o Mapa for modificado externamente, o estado interno do registro muda:

Mapamapa = novo HashMap<>();

map.put('1' 'primeiro');


Aluno s = novo Aluno(mapa 'ABC' 101);


// Muda o estado interno — NÃO é seguro

map.put('2' 'segundo');

s.metadata().put('3' 'terceiro');

Observação : use registro apenas se todos os campos forem de tipos imutáveis, como String int ou outros registros.