CodeGym /Cursos /JAVA 25 SELF /Immutability — imutabilidade de classes record

Immutability — imutabilidade de classes record

JAVA 25 SELF
Nível 22 , Lição 1
Disponível

1. Classes record e imutabilidade

Imutabilidade — é a propriedade de um objeto em que seu estado não pode ser alterado após a criação. Em outras palavras: depois que o objeto é criado, você não pode mudar seus dados internos. Ponto final! É como se estivesse gravado em pedra.

Imagine uma passagem de trem. Assim que ela é impressa, você não pode alterar a data ou o local de partida (bom, a menos que não consideremos artimanhas com Photoshop). A passagem é um objeto imutável. Se você quer outra passagem — compra uma nova.

Em programação, tais objetos são chamados de immutable objects. Eles protegem o programa contra alterações acidentais e tornam o código mais seguro e previsível.

Características de um objeto imutável

  • Todos os campos do objeto são final (podem ser atribuídos apenas uma vez, geralmente no construtor).
  • Não há setters (métodos que mudam valores de campos).
  • Todos os métodos que retornam dados internos ou devolvem cópias, ou os próprios dados também são imutáveis.

As classes record em Java foram criadas justamente para permitir criar estruturas de dados imutáveis de forma simples e sem dor.

Por que um record é imutável?

  • Todos os componentes de um record são final.
    Quando você declara um record, o compilador automaticamente torna todos os seus campos private final. Isso significa que, após criar o objeto, você não conseguirá alterar seus campos.
  • Não há setters.
    Em uma classe record, você não pode adicionar um método setX(int x) — o compilador não permitirá alterar o valor de um campo depois que o objeto for criado.
  • O construtor atribui valores apenas uma vez.
    Todos os valores são definidos somente no momento da criação do objeto.

Exemplo

public record Point(int x, int y) {}

Point p = new Point(5, 10);
// p.x = 7; // Erro de compilação: o campo x tem acesso private e é final
// p.x(7);  // Erro: não há setter!
System.out.println(p.x()); // 5

Uma tentativa de alterar um campo ou chamar um setter inexistente resulta em erro de compilação. Java aplica estritamente o contrato de imutabilidade.

2. Vantagens de objetos imutáveis

Segurança em ambientes multithread

Em programas multithread (e hoje a maioria é assim!), objetos imutáveis são como um colete à prova de balas. Se o objeto não pode ser alterado, várias threads podem lê-lo tranquilamente, sem medo de que alguém mude os dados ao mesmo tempo. Você não precisa sincronizar o acesso nem se preocupar com condições de corrida.

Fato: muitas classes da biblioteca padrão do Java, amplamente usadas em cenários multithread, ou são imutáveis, ou são especialmente protegidas contra mudanças.

Código mais fácil de entender

Se o objeto é imutável, você sempre tem a garantia: ao passá-lo para outro método ou classe — ele não mudará “pelas suas costas”. Isso facilita muito a leitura e a depuração do código. Não é preciso adivinhar quem e onde poderia ter alterado um campo — ninguém pôde!

Ótimos como chaves em coleções

Objetos imutáveis são excelentes para uso como chaves em coleções como HashMap ou HashSet. Por quê? Porque seus equals e hashCode dependem apenas de campos que não mudam. Assim, o objeto não “se perde” na coleção por ter seu estado alterado.

Menos bugs ocultos

É fácil estragar um objeto mutável, passando a referência para o lugar errado. Já um objeto imutável é como um livro impresso: ninguém pode arrancar ou reescrever uma página.

3. Comparação com classes comuns

Vamos comparar o comportamento de uma classe comum e de uma classe record. Como exemplo, vamos pegar um modelo simples de ponto no plano.

Classe comum (mutável)

public class PointClass {
    private int x;
    private int y;

    public PointClass(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { 
        return x; 
    }
    public int getY() { 
        return y; 
    }

    public void setX(int x) { 
        this.x = x; 
    }
    public void setY(int y) { 
        this.y = y; 
    }
}

Você pode criar um objeto e depois mudar seu estado quantas vezes quiser:

PointClass p = new PointClass(1, 2);
p.setX(10); // p.x agora é 10

Classe record (imutável)

public record Point(int x, int y) {}

Você cria o objeto — e pronto, ele permanecerá para sempre como foi criado:

Point p = new Point(1, 2);
// p.x = 10;   // Erro! Sem acesso ao campo
// p.x(10);    // Erro! Não há setter

Tabela: comparação de comportamento

Classe comum Classe record
Campos quaisquer apenas private final
Setters podem ser adicionados não podem ser adicionados
Mutabilidade mutável (mutable) imutável
Geração automática não sim (equals, hashCode, toString)

4. Prática: como utilizar a imutabilidade de classes record

Vamos aplicar uma classe record em um pequeno exemplo. Suponha que temos um app bancário e queremos armazenar informações sobre uma transação:

public record Transaction(String fromAccount, String toAccount, double amount) {}

Criando um objeto:

Transaction t = new Transaction("12345", "67890", 1500.0);
System.out.println(t);
// Transaction[fromAccount=12345, toAccount=67890, amount=1500.0]

Vamos tentar “transferir” o dinheiro para outra conta:

// t.toAccount = "11111"; // Erro! Campo final, sem acesso
// t.toAccount("11111");  // Erro! Não há setter

Se precisarmos de outra transação — criamos um novo objeto:

Transaction t2 = new Transaction(t.fromAccount(), "11111", t.amount());

Importante: imutabilidade não significa “incômodo”. É apenas um estilo diferente de trabalhar: se você precisa de um novo estado — crie um novo objeto.

5. Particularidades da imutabilidade: o que lembrar

Imutabilidade — nem sempre é absoluta!

Uma classe record garante que seus campos não mudarão. Mas se um campo for uma referência a um objeto mutável (por exemplo, um array ou uma classe comum), o conteúdo desse objeto pode ser alterado.

Exemplo com array

public record DataHolder(int[] data) {}

int[] arr = {1, 2, 3};
DataHolder holder = new DataHolder(arr);
arr[0] = 99;
System.out.println(holder.data()[0]); // 99! O array foi alterado

Conclusão: se você quer imutabilidade de verdade, use apenas tipos imutáveis (String, Integer, outros records etc.) ou faça cópias defensivas de objetos mutáveis dentro do construtor canônico da classe record. Por exemplo:

int[] copy = Arrays.copyOf(data, data.length);

6. Como tornar uma classe comum imutável

Se você quiser tornar uma classe comum imutável (immutable), será preciso fazer manualmente:

  • Marcar todos os campos como private final,
  • Não adicionar setters,
  • Inicializar todos os campos apenas via construtor,
  • Se o campo for um objeto mutável, fazer cópia defensiva (defensive copy).

Exemplo

public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { 
        return name; 
    }
    public int age() { 
        return age; 
    }
}

Convenhamos, com um record isso fica mais simples e curto:

public record User(String name, int age) {}

7. Erros comuns ao trabalhar com classes record imutáveis

Erro nº 1: tentativa de alterar um campo após a criação.
Iniciantes frequentemente tentam escrever p.x = 42; ou p.x(42); para um objeto record. Mas o compilador dirá imediatamente: “Assim não dá! O campo é final, não há setter”.

Erro nº 2: uso de objetos mutáveis como componentes de um record.
Se você adicionou em um record um campo do tipo List, Map, array ou outro objeto mutável, o próprio record não o protegerá contra mudanças no conteúdo desse objeto. Por exemplo, se você tem um record User(List<String> hobbies), alguém pode adicionar ou remover um elemento da lista, e isso mudará o estado do seu objeto record. Para evitar isso, use coleções imutáveis (List.copyOf, Collections.unmodifiableList) ou faça cópias das coleções dentro do construtor do record.

Erro nº 3: compreensão equivocada da imutabilidade.
Alguns pensam que, se o objeto é um record, ele está protegido de quaisquer mudanças. Na verdade, se os campos forem referências a objetos mutáveis, o conteúdo deles pode ser alterado, o que levará a bugs inesperados.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION