No ecossistema Java, os records surgiram como uma solução elegante para um problema antigo: a proliferação de código repetitivo (ou boilerplate). Antes dessa feature, classes simples, criadas apenas para transportar dados, como os famosos DTOs (Data Transfer Objects), exigiam uma implementação extensa e manual de métodos como equals(), hashCode() e toString(). Com a introdução dos records, essa implementação tornou-se significativamente mais concisa e eficiente, aumentando a produtividade e reduzindo erros.
O que é um record em Java?
É um tipo especial de classe, que são imutáveis por padrão e foi introduzida experimentalmente no java 14, tornando-se estável no java 16. Seu objetivo e simplificar a criação de classes que serve apenas para carregar dados.
O que é um DTO (Data Transfer Object)?
E um padrão de projeto, utilizado para não expor a camada de negocio da aplicação. E um objeto simples, contem apenas dados, utilizado para transportar dados entre camadas da aplicação e evitar a exposição de dados sensíveis.
Entendendo a estrutura de um record.
public record User(){}
public → pode ser acessado de qualquer lugar.
record → define que é um tipo record.
User → nome do record.
Exemplo de record com componentes:
public record User(String name, String email, LocalDate creationDate){
}
O compilador alem de gerar os métodos equals(), hashCode() e toString(), também gera os métodos getters name(), email() e creationDate() que são os componentes do record (equivalem a atributos de classes comum em java). Não gera setters, pois os dados em um record são considerados imutáveis.
Record como DTO (O CASAMENTO PERFEITO)
O exemplo a seguir apresenta uma comunicação simples, com o objetivo de transportar dados sem expor a classe de domino e utilizando a simplicidade do record.
Criando a classe de domínio User.
public class User {
private String name;
private String pass;
private String email;
private final LocalDate creationDate = LocalDate.now();
//getter e setter
}
Criando nosso record, que vai representar DTO. Aqui protegeremos nossa senha(pass) de usuário, passando somente os componentes name, email e creationDate. Implementado um método estático (from) que vai retornar um novo UserDTO e recebera um User como parâmetro.
public record UserDTO(String name, String email, LocalDate creationDate){
public static UserDTO from(User user) {
return new UserDTO(
user.getName(),
user.getEmail(),
user.getCreationDate());
}
}
Como resultado temos:
public class Principal {
public static void main(String[] args) {
User user1 = new User("Joao","123","joao@mail");
User user2 = new User("Jose","123","jose@mail");
User user3 = new User("Maria","123","maria@mail");
List<User> users = List.of(user1, user2, user3);
User fidUser = users.get(2);
UserDTO dto = UserDTO.from(fidUser);
System.out.println(dto);
}
}
Aqui simulamos nossa repositório de user, criamos três usuários, adicionamos eles a coleção de usuários utilizando List e simulamos a consulta de um usuário passando o index.
User user1 = new User("Joao","123","joao@mail");
User user2 = new User("Jose","123","jose@mail");
User user3 = new User("Maria","123","maria@mail");
List<User> users = List.of(user1, user2, user3);
User fidUser = users.get(2);
Agora controlamos o que vai ser trafegado entre o nosso domínio e a camada de apresentação.
UserDTO dto = UserDTO.from(fidUser);
Utilizamos uma impressão simples no console para representar nossa camada de apresentação.
System.out.println(dto);
Retornando um UserDTO no console com name, email e dateCreation, protegemos assim nossa senha(pass) e protegendo nossa camada de domínio User.
UserDTO[name=Maria, email=maria@mail, creationDate=2025-10-12]
Top comments (0)