1. Estendendo a classe record: métodos adicionais
Posso adicionar métodos a um record?
Claro! Um Record é como um apartamento já reformado: as paredes e o piso já estão prontos e não podem ser trocados, mas ninguém proíbe você de organizar os móveis do seu jeito. Dentro de um record você pode declarar métodos comuns, métodos estáticos e até armazenar constantes. Isso significa que a lógica de negócio não precisa ser movida para classes “utilitárias” separadas — ela pode ser integrada diretamente no próprio record.
Exemplo: método de cálculo da distância entre pontos
Suponha que temos um record para um ponto no plano:
public record Point(int x, int y) {
// Método adicional
public double distanceTo(Point other) {
int dx = this.x - other.x;
int dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
}
Agora você pode fazer assim:
Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);
System.out.println(p1.distanceTo(p2)); // 5.0
Como você pode ver, uma classe record pode ser “enriquecida” com seus próprios métodos — e isso é muito conveniente!
Exemplo: método estático
public record Rectangle(int width, int height) {
public int area() {
return width * height;
}
public static Rectangle square(int size) {
return new Rectangle(size, size);
}
}
Agora é possível criar um “quadrado” com uma única chamada:
Rectangle r = Rectangle.square(5);
System.out.println(r.area()); // 25
2. Construtor compacto e validação de dados
Para que serve o “construtor compacto”?
O construtor canônico de um record é criado automaticamente e atribui os parâmetros aos campos. Mas às vezes queremos adicionar uma verificação dos dados de entrada (por exemplo, proibir coordenadas negativas).
Em uma classe comum, escreveríamos:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
if (x < 0 || y < 0) throw new IllegalArgumentException();
this.x = x;
this.y = y;
}
// ...
}
Em um record, é possível declarar um construtor compacto — sem repetir a lista de parâmetros e sem atribuir os campos explicitamente (isso é feito pelo compilador).
Sintaxe do construtor compacto
public record Point(int x, int y) {
public Point {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Coordinates must be non-negative");
}
// Não é necessário escrever: this.x = x; this.y = y;
}
}
- Os parâmetros do construtor coincidem automaticamente com os componentes do record.
- As atribuições this.x = x e this.y = y são feitas pelo compilador automaticamente após a execução do corpo do construtor (ou após a saída bem-sucedida dele).
- Se uma exceção for lançada, o objeto não será criado.
Exemplo com validação
Point p1 = new Point(3, 5); // OK
Point p2 = new Point(-1, 2); // Lança IllegalArgumentException!
Posso declarar um “construtor comum”?
Sim! Se você precisar criar um construtor com outra lista de parâmetros ou lógica adicional, declare-o explicitamente:
public record Range(int from, int to) {
public Range(int size) {
this(0, size); // chama o construtor principal
}
}
3. Restrições de classes record
Ao contrário das classes comuns, as classes record têm uma série de restrições. É importante lembrar disso para não se surpreender com erros de compilação.
Apenas componentes — nenhum campo não estático adicional
Em uma classe record não é permitido declarar novos campos não estáticos:
public record Person(String name, int age) {
// int id; // Erro de compilação! Não é permitido adicionar campos não estáticos.
}
É permitido declarar campos e métodos estáticos:
public record Person(String name, int age) {
public static final String SPECIES = "Homo sapiens";
}
Record é sempre final
Uma classe Record não pode ser classe pai (não é possível herdar dela) e ela própria não pode estender explicitamente outra classe (exceto a herança implícita de java.lang.Record). Isso significa que uma classe record é sempre uma estrutura “final”.
public record User(String login) { }
// public class Admin extends User {} // Erro: não é possível herdar de um record!
É possível implementar interfaces
Uma classe Record pode implementar interfaces:
public interface Printable {
void print();
}
public record Invoice(int amount) implements Printable {
@Override
public void print() {
System.out.println("Valor: " + amount);
}
}
4. Exemplos: records avançados em tarefas reais
Vamos analisar alguns exemplos práticos em que métodos adicionais e construtores compactos tornam a classe record realmente útil.
Record com método calculado
public record Circle(double x, double y, double radius) {
public double area() {
return Math.PI * radius * radius;
}
public double distanceTo(Circle other) {
double dx = x - other.x;
double dy = y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
}
Record com validação
public record Email(String value) {
public Email {
if (value == null || !value.contains("@")) {
throw new IllegalArgumentException("E-mail inválido: " + value);
}
}
}
Agora não é possível criar um e-mail inválido:
Email e1 = new Email("test@example.com"); // OK
Email e2 = new Email("not-an-email"); // Lança IllegalArgumentException
Record com métodos estáticos adicionais
public record Temperature(double celsius) {
public static Temperature fromFahrenheit(double fahrenheit) {
return new Temperature((fahrenheit - 32) * 5 / 9);
}
public double toFahrenheit() {
return celsius * 9 / 5 + 32;
}
}
Uso:
Temperature t = Temperature.fromFahrenheit(98.6);
System.out.println(t.celsius()); // 37.0
System.out.println(t.toFahrenheit()); // 98.6
5. Construtor compacto: nuances e restrições
Quando usar o construtor compacto?
- Quando for necessário verificar a correção dos dados (validação).
- Quando for necessário alterar valores antes de salvá-los (por exemplo, arredondar um número ou colocar uma string em maiúsculas).
- Quando você quiser evitar duplicação da lista de parâmetros.
Características de funcionamento
- No construtor compacto não é permitido atribuir valores aos componentes explicitamente (this.x = ...) — isso causará erro de compilação, pois o compilador faz a atribuição após a execução do corpo do construtor.
- No construtor compacto não é possível alterar os nomes dos parâmetros — eles sempre coincidem com os nomes dos componentes do record.
Exemplo: arredondamento automático
public record Money(double amount) {
public Money {
amount = Math.round(amount * 100) / 100.0; // Arredondamos para centavos
}
}
6. Prática: evoluindo o aplicativo didático
Suponha que estamos implementando operações bancárias em um aplicativo didático. Temos uma classe record Transaction, que armazena o valor, o remetente e o destinatário.
public record Transaction(String from, String to, double amount) {
public Transaction {
if (amount <= 0) throw new IllegalArgumentException("O valor deve ser positivo");
if (from == null || to == null) throw new IllegalArgumentException("Os campos não podem ser null");
}
public String description() {
return String.format("Transferência de %.2f de %s para %s", amount, from, to);
}
}
Uso:
Transaction t = new Transaction("Alice", "Bob", 150.0);
System.out.println(t.description()); // Transferência de 150.00 de Alice para Bob
Uma tentativa de criar uma transação inválida resultará em erro:
Transaction t2 = new Transaction("Alice", "Bob", -10.0); // IllegalArgumentException
Tabela: o que pode e o que não pode em uma classe record
| Permitido em uma classe record | Não permitido em uma classe record |
|---|---|
| Métodos comuns | Novos campos não estáticos |
| Métodos e campos estáticos | Herdar de outras classes |
| Implementar interfaces | Ser superclasse para outras classes |
| Construtores compactos e comuns | Alterar componentes após a criação |
| Sobrescrever métodos | Usar setters |
7. Erros típicos ao trabalhar com classes record com corpo customizado
Erro nº 1: tentativa de adicionar um campo não estático.
Iniciantes muitas vezes tentam adicionar à classe record “mais um campo para a lógica interna” — por exemplo, um contador ou um cache. Isso não funciona: o compilador emitirá um erro imediatamente. Se você precisa manter estado adicional, provavelmente uma classe record não é a melhor escolha.
Erro nº 2: esquecer a validação no construtor compacto.
Se você quer que o objeto seja sempre válido, faça a verificação no construtor compacto. Não confie no fato de que “o usuário não vai digitar algo errado”.
Erro nº 3: tentar alterar um componente após a criação.
Os campos da classe record são final — não podem ser alterados nem diretamente nem por meio de métodos. Se você precisa de uma estrutura mutável, use uma classe comum.
Erro nº 4: duplicar a lógica no(s) método(s) e no construtor.
Às vezes a lógica de validação e a lógica de cálculo acabam duplicadas tanto em métodos quanto no construtor. É melhor concentrar toda a validação no construtor e deixar os métodos para a lógica de negócio “limpa”.
Erro nº 5: esquecer as restrições de herança.
Uma classe Record é sempre final — não é possível criar uma subclasse dela. Se você está projetando uma hierarquia que precisa de subclasses, use classes comuns.
GO TO FULL VERSION