CodeGym /Cursos /JAVA 25 SELF /Diferenças entre record e class, limitações de record

Diferenças entre record e class, limitações de record

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

1. Comparação entre record e class: quais são as principais diferenças?

Em Java, temos duas formas principais de descrever nossos tipos de dados: por meio de classes comuns (class) e por meio de classes record (record). À primeira vista, ambas as opções permitem armazenar e processar dados. Mas, se olharmos um pouco mais fundo, há mais diferenças do que pode parecer!

Tabela de diferenças: class vs record

Característica Classe comum (class) Classe record (record)
Mutabilidade Qualquer: pode tornar campos final ou não Imutável: todos os campos são final
Herança Pode herdar (extends), não é final por padrão Sempre final, não pode ser superclasse
Campos Qualquer: estáticos, não estáticos, final ou não-final, quaisquer tipos Apenas componentes do record (private final), além de campos estáticos
Getters/setters Escrevemos manualmente (ou geramos com Lombok) Getters são criados automaticamente (o nome do campo é o nome do método), não há setters
equals/hashCode/toString Normalmente escrevemos/geramos (equals, hashCode, toString) Gerados automaticamente com base em todos os componentes
Construtores Quaisquer, quantos forem necessários Um principal (para todos os componentes), é possível adicionar um construtor compacto
Interfaces Pode implementar Pode implementar
Métodos adicionais Quaisquer É possível adicionar, mas apenas métodos (não campos)
Uso em coleções É possível, mas é preciso implementar corretamente equals/hashCode Perfeito para chaves/valores, tudo já está implementado

Exemplo ilustrativo

Classe comum:


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

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

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

    @Override
    public boolean equals(Object o) { /* ... */ }
    @Override
    public int hashCode() { /* ... */ }
    @Override
    public String toString() { /* ... */ }
}

Classe record:


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

Pronto! Uma única linha de código — e obtemos o mesmo (e até melhor). E sem riscos de esquecer de implementar algo importante.

2. Restrições das classes record

As classes record não são apenas “sintaxe curta”, mas uma abstração própria com regras rígidas. Vamos analisá-las em detalhes.

Record é sempre final

Por definição, uma classe record é sempre final. Isso significa que você não pode criar uma subclasse de um record:


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

// public class ColoredPoint extends Point { } // Erro de compilação!

Se você precisa estender o comportamento, use classes comuns ou composição (encapsule o record em uma classe).

Record não pode ser superclasse

Uma classe record não pode ser pai de outras classes; ela é sempre final. Isso é lógico: se fosse possível, alguém poderia adicionar um campo mutável — e toda a ideia de “dados imutáveis” cairia por terra.

Apenas campos final (componentes)

Todos os componentes do record são declarados no cabeçalho e, por padrão, são private final. Você não pode adicionar campos não estáticos no corpo do record:


public record User(String login, String email) {
    // int counter; // Erro! Não é permitido adicionar campos não estáticos
    static int totalUsers = 0; // Pode, é um campo estático
}

Sem setters

Uma classe record não pode ter setters para componentes. Qualquer tentativa de adicionar um método como setX(int x) será inútil: você não conseguirá alterar o valor do campo após a criação do objeto.


public record Point(int x, int y) {
    // public void setX(int x) { this.x = x; } // Erro: não é possível alterar um campo final
}

Sem construtor vazio

Uma classe record sempre tem apenas um construtor principal, que aceita valores para todos os componentes. Não é possível criar um record sem informar todos os dados:


Point p = new Point(1, 2);  // OK
// Point p = new Point();   // Erro: não há construtor sem parâmetros

Sem inicializadores não estáticos

Uma classe record não pode conter inicializadores não estáticos (aqueles escritos entre chaves fora dos métodos):


public record User(String login) {
    // { /* ... */ } // Erro: inicializadores não estáticos são proibidos
}

Restrições de herança

Uma classe record não pode herdar explicitamente outra classe (exceto java.lang.Record, que é oculto como a classe base de todos os record). Mas implementar interfaces — pode sim!


public interface Printable {
    void print();
}

public record Book(String title) implements Printable {
    @Override
    public void print() {
        System.out.println("Imprimindo o livro: " + title);
    }
}

Não serve para lógica de negócio complexa

record é sobre dados, não sobre comportamento. Se o seu objeto tem lógica complexa, estado mutável, “ciclo de vida” ou um monte de dependências — o record não vai ajudar. É melhor usar uma classe comum.

3. Quando usar classes record?

  • DTO (Data Transfer Object): para transferir dados imutáveis entre camadas da aplicação, serviços, microsserviços ou controladores REST (por exemplo, em respostas JSON).
  • Value Object: objetos definidos apenas por seus valores.
  • Chaves e valores em coleções: quando é importante a implementação correta de equals e hashCode (por exemplo, para uso em HashMap ou Set).
  • Resultados de cálculos: quando é preciso retornar vários valores de um método de uma só vez (por exemplo, record Pair<T, U>(T first, U second)).

Exemplo: DTO para controlador REST


public record UserDto(String login, String email) { }

Agora você pode retornar com segurança um objeto desse tipo do controlador, sem medo de que alguém altere seus campos.

Exemplo: chave para HashMap


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

Map<Point, String> pointNames = new HashMap<>();
pointNames.put(new Point(1, 2), "A");
pointNames.put(new Point(3, 4), "B");

// Tudo funciona corretamente: equals e hashCode já estão implementados!

4. Quando NÃO usar classes record

  • Estado mutável: se pelo menos um campo precisar mudar após a criação do objeto.
  • Lógica complexa: se o objeto tiver comportamento complexo, muitos métodos, objetos aninhados com estado mutável.
  • Herança: se for necessária uma hierarquia de classes, classes base abstratas, sobrescrita de métodos.
  • Entidades de negócio: por exemplo, objetos que vivem no banco de dados e têm um identificador único.

Exemplo: quando é preciso uma classe comum


public class Account {
    private String id;
    private int balance;

    public Account(String id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void deposit(int amount) { balance += amount; }
    public void withdraw(int amount) { balance -= amount; }
    // getters, setters, equals, hashCode, toString...
}

Aqui fica claro que o estado do objeto muda — record não é adequado.

5. Exemplos práticos: escolhendo entre record e class

Exemplo 1: record — escolha ideal


public record Rectangle(int width, int height) {
    public int area() {
        return width * height;
    }
}
  • O retângulo é definido apenas pela largura e altura.
  • Não é necessário alterar esses valores após a criação.
  • É possível adicionar um método útil area().
  • O restante o Java faz por você.

Exemplo 2: class — a melhor opção


public class MutableRectangle {
    private int width;
    private int height;

    public MutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void setWidth(int width) { this.width = width; }
    public void setHeight(int height) { this.height = height; }

    public int area() { return width * height; }
}

Precisa alterar as dimensões do retângulo após a criação? Use uma classe comum.

6. Erros comuns ao trabalhar com classes record

Erro nº 1: tentativa de adicionar um campo não estático.
Uma classe record não permite declarar campos não estáticos fora da lista de componentes. Se tentar, o compilador emitirá um erro. Por exemplo:


public record City(String name) {
    // int population; // Erro!
}

Erro nº 2: tentar adicionar um setter.
Record não suporta setters para componentes. Qualquer tentativa de alterar o valor de um campo após a criação do objeto resulta em erro de compilação.

Erro nº 3: tentar herdar um record ou herdar de um record.
Record é sempre final. Não é possível herdar de um record, e um record não pode herdar outra classe (além do java.lang.Record oculto).

Erro nº 4: usar record para objetos mutáveis.
Se você pretende mudar o estado do objeto após a criação — record não é para você! Use uma classe comum.

Erro nº 5: esquecer as restrições do construtor.
Uma classe record deve ter obrigatoriamente um construtor que receba valores para todos os componentes. Não existe construtor sem parâmetros!

1
Pesquisa/teste
Classes record, nível 22, lição 4
Indisponível
Classes record
Classes record
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION