Java Records vs Lombok: Guia Definitivo para Escolher a Ferramenta Certa



This content originally appeared on DEV Community and was authored by Diego de Sousa Brandão

Introdução

A eterna busca por reduzir o código boilerplate em Java ganhou duas ferramentas poderosas: Java Records (introduzidos no Java 14 e estabilizados no Java 16) e Lombok (biblioteca amplamente adotada há anos). Embora ambos ataquem o mesmo problema – a verbosidade do Java – eles fazem isso de maneiras fundamentalmente diferentes, com implicações importantes para o design e arquitetura do seu código.

Este artigo vai além da comparação superficial de sintaxe e mergulha nas diferenças semânticas, casos de uso práticos e trade-offs de cada abordagem.

Records Java: Mais que Redução de Boilerplate

O que são Records?

Records não são apenas uma forma concisa de criar classes – eles são tipos de produto com semântica específica. Segundo a JEP 395:

“Records são portadores transparentes de dados imutáveis”

A palavra-chave aqui é transparente. Records seguem o princípio:

“A API de um record modela o estado, todo o estado, e nada além do estado”

Exemplo Básico

// Antes: 40+ linhas de código boilerplate
public class Pessoa {
    private final String nome;
    private final int idade;

    public Pessoa(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public String getNome() { return nome; }
    public int getIdade() { return idade; }

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

// Depois: 1 linha
public record Pessoa(String nome, int idade) {}

Características dos Records

  1. Imutabilidade por Design: Todos os campos são private final
  2. Transparência: Não há estado oculto
  3. Construtores Canônicos: Gerados automaticamente
  4. Métodos Padrão: equals(), hashCode(), toString() implementados automaticamente
  5. Acessores: Métodos com o mesmo nome dos componentes

Lombok: Flexibilidade Através de Anotações

O que é Lombok?

Lombok é uma biblioteca que gera código em tempo de compilação através de anotações. Não impõe semântica específica – é pura redução de boilerplate.

Exemplo com Lombok

@Data
public class Produto {
    private String nome;
    private double preco;

    // Método personalizado
    public void aplicarDesconto(double desconto) {
        this.preco -= desconto;
    }
}

Principais Anotações

  • @Data: Gera getters, setters, equals(), hashCode(), toString()
  • @Value: Classe imutável (equivalente a um record)
  • @Builder: Padrão Builder
  • @Slf4j: Injeção de logger
  • @Getter/@Setter: Getters e setters específicos

Comparação Detalhada

Aspecto Records Lombok
Semântica Tipos de produto transparentes Geração de código
Imutabilidade Por padrão Opcional (@Value)
Flexibilidade Limitada Alta
Herança Não suportada Suportada
Estado Oculto Proibido Permitido
Dependência Externa Não Sim
Versão Java 14+ (estável no 16+) Qualquer
Desempenho Nativo Geração em tempo de compilação

Quando Usar Records

✅ Use Records para:

1. DTOs (Data Transfer Objects)

public record UsuarioDTO(String nome, String email, LocalDate nascimento) {}

2. Objetos de Valor

public record Dinheiro(BigDecimal valor, String moeda) {}

3. Respostas de API

public record ApiResponse<T>(T dados, int status, String mensagem) {}

4. Configurações Imutáveis

public record DatabaseConfig(String url, String usuario, int timeout) {}

5. Entidades JPA Simples (com limitações)

@Entity
public record Produto(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id,
    String nome,
    BigDecimal preco
) {}

❌ Evite Records quando:

  • Precisar de mutabilidade
  • Necessitar de herança
  • Requerer estado oculto
  • Trabalhar com frameworks que exigem construtores sem argumentos
  • Precisar de métodos de ciclo de vida JPA complexos

Quando Usar Lombok

✅ Use Lombok para:

1. Entidades JPA Complexas

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Usuario {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nome;
    private String email;

    @OneToMany(mappedBy = "usuario", cascade = CascadeType.ALL)
    private List<Pedido> pedidos = new ArrayList<>();
}

2. Modelos de Negócio com Comportamento

@Data
@Slf4j
public class ContaBancaria {
    private String numero;
    private BigDecimal saldo;

    public void sacar(BigDecimal valor) {
        log.info("Sacando {} da conta {}", valor, numero);
        if (saldo.compareTo(valor) >= 0) {
            saldo = saldo.subtract(valor);
        } else {
            throw new IllegalArgumentException("Saldo insuficiente");
        }
    }
}

3. Builders Complexos

@Builder
@Data
public class Relatorio {
    private String titulo;
    private LocalDate dataInicio;
    private LocalDate dataFim;
    private List<String> colunas;
    private Map<String, Object> parametros;
}

// Uso
Relatorio relatorio = Relatorio.builder()
    .titulo("Vendas Mensais")
    .dataInicio(LocalDate.now().minusMonths(1))
    .dataFim(LocalDate.now())
    .colunas(Arrays.asList("produto", "quantidade", "valor"))
    .build();

❌ Evite Lombok quando:

  • O projeto exigir zero dependências externas
  • Houver problemas de compatibilidade com IDEs
  • A equipe não estiver familiarizada com a biblioteca
  • Precisar de garantias semânticas específicas

Casos de Uso Práticos

Cenário 1: Sistema de E-commerce

// DTO para resposta da API - USE RECORD
public record ProdutoResponse(
    Long id,
    String nome,
    BigDecimal preco,
    String categoria
) {}

// Entidade JPA com comportamento - USE LOMBOK
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Produto {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nome;
    private BigDecimal preco;
    private String categoria;
    private Integer estoque;

    public void reduzirEstoque(int quantidade) {
        if (estoque < quantidade) {
            throw new IllegalArgumentException("Estoque insuficiente");
        }
        this.estoque -= quantidade;
    }
}

Cenário 2: Sistema Bancário

// Objeto de valor imutável - USE RECORD
public record Moeda(String codigo, String nome, String simbolo) {}

// Entidade complexa com estado mutável - USE LOMBOK
@Entity
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
@Slf4j
public class Conta {
    @Id
    @EqualsAndHashCode.Include
    private String numero;

    private BigDecimal saldo;
    private String titular;

    @OneToMany(mappedBy = "conta", cascade = CascadeType.ALL)
    private List<Transacao> transacoes = new ArrayList<>();

    public void transferir(BigDecimal valor, Conta destino) {
        log.info("Transferindo {} de {} para {}", valor, this.numero, destino.numero);
        // Lógica de transferência
    }
}

Híbrido: Usando Records e Lombok Juntos

// Record com anotações do Lombok para funcionalidades adicionais
@Slf4j
public record ProcessamentoResult(
    String id,
    LocalDateTime timestamp,
    boolean sucesso,
    String mensagem
) {
    public ProcessamentoResult {
        log.info("Resultado do processamento {}: {}", id, sucesso ? "SUCESSO" : "FALHA");
    }
}

O Futuro: Spring Data Commons Recomenda Records

O Spring Framework oficialmente recomenda Records para DTOs e projeções:

// Recomendação oficial do Spring Data
public record NamesOnly(String firstname, String lastname) {}

// Em vez de usar Lombok
@Value
class NamesOnly {
    String firstname, lastname;
}

Diretrizes para Tomada de Decisão

Escolha Records quando:

  • ✅ Estiver no Java 16+
  • ✅ Precisar de objetos imutáveis
  • ✅ Quiser garantias semânticas fortes
  • ✅ Criar DTOs ou objetos de valor
  • ✅ Buscar máxima performance (sem overhead de biblioteca)

Escolha Lombok quando:

  • ✅ Precisar de flexibilidade máxima
  • ✅ Trabalhar com herança
  • ✅ Requerer mutabilidade
  • ✅ Usar versões antigas do Java
  • ✅ Precisar de funcionalidades como @builder ou @Slf4j

Use Ambos quando:

  • ✅ Records para DTOs e objetos de valor
  • ✅ Lombok para entidades e modelos de negócio
  • ✅ Aproveitar o melhor de cada abordagem

Migração Gradual

Estratégia de Migração para Records:

  1. Identifique candidatos: DTOs, objetos de valor, classes imutáveis simples
  2. Migre gradualmente: Comece com classes menos críticas
  3. Teste thoroughly: Verifique serialização e frameworks
  4. Monitore performance: Records podem ter características diferentes

Exemplo de Migração:

// Antes (Lombok)
@Value
public class Endereco {
    String rua;
    String cidade;
    String cep;
}

// Depois (Record)
public record Endereco(String rua, String cidade, String cep) {}

Considerações de Performance

Records:

  • ✅ Sem overhead de biblioteca
  • ✅ Otimizações do compilador
  • ⚠ Criação de objetos em atualizações (imutabilidade)

Lombok:

  • ✅ Código gerado otimizado
  • ⚠ Dependência de bibliotecas externas
  • ⚠ Possível overhead de reflection em algumas situações

Conclusão

A escolha entre Records e Lombok não é uma questão de “melhor” ou “pior”, mas sim de adequação ao contexto:

  • Records brilham quando você precisa de semântica forte, imutabilidade e transparência
  • Lombok excele quando você precisa de flexibilidade, mutabilidade e compatibilidade com versões antigas

A tendência é usar ambos estrategicamente:

  • Records para DTOs, objetos de valor e dados imutáveis
  • Lombok para entidades complexas e modelos de negócio

Com o Java evoluindo e mais projetos adotando versões recentes, Records estão se tornando a escolha padrão para muitos casos de uso. No entanto, Lombok continuará relevante para cenários que demandam máxima flexibilidade.

A chave é entender que essas ferramentas resolvem problemas similares de maneiras diferentes, e a escolha certa depende das necessidades específicas do seu projeto, versão do Java e arquitetura da aplicação.

Referências Bibliográficas

  1. Replace Some Lombok Features with record in Java 17: A Modern Alternative
  2. https://github.com/spring-projects/spring-data-commons/commit/959bde4d37b10342d963cf47d0bbf1b68000ff6a
  3. https://github.com/spring-projects/spring-data-commons/pull/2794
  4. https://medium.com/@fabio.alvaro/records-ou-lombok-qual-a-melhor-escolha-para-o-seu-projeto-java-b0d1fde468f2
  5. https://javatechonline.com/java-records-vs-jpa-entities-and-lombok/


This content originally appeared on DEV Community and was authored by Diego de Sousa Brandão