Java

Tutorial Java: Lombok para Java

Aprende a usar Lombok en Java para reducir código repetitivo con @Data, @Getter, @Setter y constructores. Mejora productividad y mantenimiento.

Aprende Java y certifícate

Qué es Lombok y qué problema soluciona

Project Lombok es una biblioteca Java que se conecta automáticamente a tu editor y herramientas de compilación, mejorando significativamente tu código Java al reducir la cantidad de código repetitivo que necesitas escribir. En esencia, Lombok actúa como un potenciador de productividad para los desarrolladores Java.

El principal problema que Lombok soluciona es la verbosidad inherente de Java. Como desarrolladores Java, frecuentemente nos encontramos escribiendo código repetitivo y ceremonioso:

  • Métodos getters y setters para cada campo
  • Constructores con diferentes combinaciones de parámetros
  • Implementaciones de equals(), hashCode() y toString()
  • Manejo de recursos con bloques try-finally
  • Código de validación de parámetros

Todo este código boilerplate (código estándar que se repite con pocas variaciones) no solo consume tiempo de escritura, sino que también:

  • Reduce la legibilidad del código al ocultar la lógica de negocio entre líneas de código utilitario
  • Aumenta la posibilidad de errores al tener que mantener manualmente estos métodos
  • Dificulta el mantenimiento cuando se añaden o modifican campos en una clase

Lombok aborda estos problemas mediante anotaciones que generan automáticamente este código repetitivo en tiempo de compilación. Por ejemplo:

// Sin Lombok: una clase POJO típica
public class Usuario {
    private Long id;
    private String nombre;
    private String email;
    
    // Constructor vacío
    public Usuario() {
    }
    
    // Constructor con todos los campos
    public Usuario(Long id, String nombre, String email) {
        this.id = id;
        this.nombre = nombre;
        this.email = email;
    }
    
    // Getters y setters (¡muchas líneas de código!)
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getNombre() {
        return nombre;
    }
    
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    // Métodos equals, hashCode y toString
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Usuario usuario = (Usuario) o;
        return Objects.equals(id, usuario.id) &&
               Objects.equals(nombre, usuario.nombre) &&
               Objects.equals(email, usuario.email);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id, nombre, email);
    }
    
    @Override
    public String toString() {
        return "Usuario{" +
               "id=" + id +
               ", nombre='" + nombre + '\'' +
               ", email='" + email + '\'' +
               '}';
    }
}

Con Lombok, el mismo código se reduce drásticamente:

// Con Lombok: la misma funcionalidad en pocas líneas
import lombok.Data;

@Data
public class Usuario {
    private Long id;
    private String nombre;
    private String email;
}

La anotación @Data genera automáticamente todos los getters, setters, constructores, equals(), hashCode() y toString() en tiempo de compilación. El bytecode final generado es prácticamente idéntico al que escribiríamos manualmente, pero con mucho menos esfuerzo y posibilidad de error.

Integración con el ecosistema Java

Lombok se integra perfectamente con:

  • IDEs: IntelliJ IDEA, Eclipse, NetBeans, VS Code (con extensiones)
  • Herramientas de construcción: Maven, Gradle
  • Frameworks: Spring, Jakarta EE, Quarkus, Micronaut

Para usar Lombok en un proyecto Maven, simplemente añadimos la dependencia:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

El ámbito provided indica que Lombok solo se necesita durante la compilación, no en tiempo de ejecución.

Beneficios clave

  • Reducción de código: Menos líneas de código para mantener y revisar.
  • Mayor enfoque en la lógica de negocio: El código importante destaca más.
  • Menor propensión a errores: El código generado automáticamente es consistente y probado.
  • Facilidad de mantenimiento: Cambiar la estructura de datos requiere menos modificaciones.
  • Compatibilidad con herramientas: Funciona con la mayoría de herramientas de análisis de código y cobertura.

Consideraciones importantes

Aunque Lombok ofrece grandes ventajas, es importante considerar algunos aspectos:

  • Curva de aprendizaje inicial: Los nuevos miembros del equipo necesitan familiarizarse con las anotaciones.
  • Dependencia de una biblioteca externa: Introduces una dependencia adicional en tu proyecto.
  • Posibles conflictos: En raras ocasiones, puede haber conflictos con otras herramientas de procesamiento de anotaciones.

Lombok es especialmente valioso en proyectos que utilizan patrones como Domain-Driven Design o arquitecturas basadas en microservicios, donde se manejan numerosas clases de modelo y DTOs (Data Transfer Objects), reduciendo significativamente la cantidad de código repetitivo y permitiendo a los desarrolladores centrarse en la lógica de negocio esencial.

La anotación @Data

La anotación @Data es una de las anotaciones más completas que ofrece Lombok, funcionando como una combinación de varias anotaciones más específicas. Cuando aplicamos @Data a una clase, estamos solicitando a Lombok que genere automáticamente un conjunto completo de funcionalidades que normalmente requeriría escribir decenas de líneas de código.

Esta anotación es esencialmente un atajo que equivale a aplicar las siguientes anotaciones simultáneamente:

  • @Getter: Para todos los campos
  • @Setter: Para todos los campos no finales
  • @ToString: Incluyendo todos los campos
  • @EqualsAndHashCode: Basado en todos los campos no estáticos
  • @RequiredArgsConstructor: Genera un constructor con los campos obligatorios (final o marcados con @NonNull)

Veamos un ejemplo práctico de cómo @Data transforma una clase:

import lombok.Data;

@Data
public class Producto {
    private Long id;
    private String nombre;
    private double precio;
    private boolean disponible;
}

Este código tan conciso genera automáticamente:

  • Métodos getter para todos los campos: getId(), getNombre(), getPrecio(), isDisponible()
  • Métodos setter para todos los campos: setId(), setNombre(), setPrecio(), setDisponible()
  • Un método toString() que incluye el nombre de la clase y todos los campos
  • Métodos equals() y hashCode() basados en todos los campos
  • Un constructor sin argumentos

Personalización de @Data

Aunque @Data aplica una configuración predeterminada, podemos personalizar su comportamiento combinándola con otras anotaciones:

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(exclude = "password")
public class Usuario {
    @EqualsAndHashCode.Include
    private Long id;
    
    private String username;
    private String password;
    private String email;
}

En este ejemplo:

  • Solo usamos el campo id para los métodos equals() y hashCode()
  • Excluimos el campo password del método toString() por razones de seguridad

Comportamiento con campos finales

Cuando usamos @Data con campos finales, Lombok adapta su comportamiento:

import lombok.Data;

@Data
public class Configuracion {
    private final String nombre;
    private final int valor;
    private boolean activa;
}

En este caso, Lombok:

  • No generará setters para los campos finales (nombre y valor)
  • Generará un constructor que acepta los campos finales como parámetros
  • Mantendrá el comportamiento normal para el campo no final (activa)

Uso con herencia

Cuando trabajamos con herencia, debemos tener cuidado con la implementación de equals() y hashCode():

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class ProductoEspecial extends Producto {
    private String caracteristicaEspecial;
}

El parámetro callSuper = true indica a Lombok que debe incluir los campos de la clase padre en los métodos equals() y hashCode(). Sin esta configuración, podríamos tener problemas de identidad al comparar objetos.

Limitaciones y consideraciones

Aunque @Data es muy útil, tiene algunas limitaciones que debemos considerar:

  • Inmutabilidad parcial: Si necesitas objetos completamente inmutables, considera usar @Value en lugar de @Data
  • Exposición de todos los campos: Genera getters y setters para todos los campos, lo que podría no ser deseable desde una perspectiva de encapsulamiento
  • Comportamiento predeterminado: A veces necesitarás personalizar el comportamiento más allá de lo que ofrece @Data

Cuándo usar @Data

La anotación @Data es ideal para:

  • Clases de modelo o entidades que representan datos
  • DTOs (Data Transfer Objects) utilizados para transferir datos entre capas
  • POJOs (Plain Old Java Objects) simples sin lógica de negocio compleja
  • Prototipos rápidos donde la productividad es prioritaria
// Ejemplo ideal para @Data: un DTO simple
import lombok.Data;

@Data
public class ClienteDTO {
    private Long id;
    private String nombre;
    private String email;
    private String telefono;
}

Cuándo evitar @Data

Hay situaciones donde es mejor usar anotaciones más específicas en lugar de @Data:

  • Cuando necesitas un control preciso sobre qué métodos se generan
  • En clases con lógica de negocio compleja donde la encapsulación es crucial
  • Cuando requieres inmutabilidad total (usa @Value en su lugar)
  • En clases que forman parte de una jerarquía de herencia compleja
// Mejor enfoque para una entidad de dominio con lógica de negocio
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString(exclude = "contraseña")
public class Usuario {
    private final Long id;
    private String nombre;
    private String contraseña;
    
    // Constructor personalizado
    public Usuario(Long id, String nombre) {
        this.id = id;
        this.nombre = nombre;
    }
    
    // Método setter personalizado con validación
    public void setNombre(String nombre) {
        if (nombre == null || nombre.trim().isEmpty()) {
            throw new IllegalArgumentException("El nombre no puede estar vacío");
        }
        this.nombre = nombre;
    }
}

Rendimiento y tamaño del bytecode

Es importante entender que Lombok no afecta el rendimiento en tiempo de ejecución, ya que todo el código se genera durante la compilación. El bytecode resultante es prácticamente idéntico al que escribirías manualmente.

Sin embargo, el uso de @Data puede aumentar ligeramente el tamaño del bytecode final, especialmente en clases con muchos campos, ya que genera métodos completos para cada funcionalidad.

Integración con IDEs

Para aprovechar al máximo @Data y otras anotaciones de Lombok, es recomendable instalar el plugin de Lombok para tu IDE:

  • En IntelliJ IDEA: Habilita el procesamiento de anotaciones y el plugin de Lombok
  • En Eclipse: Instala el plugin de Lombok ejecutando el JAR de Lombok
  • En VS Code: Instala la extensión "Lombok Annotations Support"

Esto permitirá que tu IDE reconozca los métodos generados por Lombok, facilitando la navegación y el autocompletado.

Las anotaciones @Getter, @Setter y de constructor

Mientras que @Data proporciona un conjunto completo de funcionalidades, Lombok también ofrece anotaciones específicas que permiten un control más granular sobre la generación de código. Estas anotaciones individuales son ideales cuando necesitamos personalizar exactamente qué código se genera para nuestras clases.

Anotación @Getter

La anotación @Getter genera automáticamente los métodos de acceso (getters) para los campos de una clase. Puede aplicarse a nivel de clase o a campos individuales:

import lombok.Getter;

public class Empleado {
    @Getter private String nombre;
    @Getter private double salario;
    private String informacionConfidencial; // Sin getter
}

En este ejemplo, Lombok generará los métodos getNombre() y getSalario(), pero no creará un getter para informacionConfidencial.

También podemos aplicar @Getter a nivel de clase para generar getters para todos los campos:

import lombok.Getter;

@Getter
public class Producto {
    private String codigo;
    private String nombre;
    private double precio;
}

Esto generará los métodos getCodigo(), getNombre() y getPrecio() automáticamente.

Personalización de getters

Podemos personalizar el comportamiento de los getters generados:

import lombok.Getter;
import lombok.AccessLevel;

public class Configuracion {
    @Getter(AccessLevel.PUBLIC) private String nombre;
    @Getter(AccessLevel.PROTECTED) private int valor;
    @Getter(AccessLevel.NONE) private boolean interno;
}

Los niveles de acceso disponibles son:

  • PUBLIC: Accesible desde cualquier lugar (predeterminado)
  • PROTECTED: Accesible desde la misma clase, paquete y subclases
  • PACKAGE: Accesible desde la misma clase y paquete
  • PRIVATE: Accesible solo desde la misma clase
  • NONE: No genera el método getter

Anotación @Setter

De manera similar, @Setter genera métodos modificadores (setters) para los campos:

import lombok.Setter;

public class Cliente {
    @Setter private String nombre;
    @Setter private String email;
    private final Long id; // No tendrá setter por ser final
}

Lombok generará los métodos setNombre(String nombre) y setEmail(String email), pero no generará un setter para id ya que es un campo final.

Al igual que con @Getter, podemos aplicar @Setter a nivel de clase:

import lombok.Setter;

@Setter
public class Pedido {
    private String referencia;
    private double total;
    private final String fechaCreacion; // No tendrá setter por ser final
}

Personalización de setters

También podemos personalizar el nivel de acceso de los setters:

import lombok.Setter;
import lombok.AccessLevel;

public class Usuario {
    @Setter private String username;
    @Setter(AccessLevel.PRIVATE) private String password;
    @Setter(AccessLevel.NONE) private String rol;
}

Combinando @Getter y @Setter

Es común combinar ambas anotaciones para generar tanto getters como setters:

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Articulo {
    private String titulo;
    private String contenido;
    private final String autor; // Tendrá getter pero no setter
}

También podemos aplicarlas de forma selectiva:

import lombok.Getter;
import lombok.Setter;

public class CuentaBancaria {
    @Getter private final String numeroCuenta;
    @Getter @Setter private double saldo;
    @Getter(AccessLevel.PRIVATE) private String pin;
    
    public CuentaBancaria(String numeroCuenta) {
        this.numeroCuenta = numeroCuenta;
    }
}

Anotaciones para constructores

Lombok ofrece varias anotaciones para generar diferentes tipos de constructores:

@NoArgsConstructor

Genera un constructor sin argumentos:

import lombok.NoArgsConstructor;

@NoArgsConstructor
public class Mensaje {
    private String asunto;
    private String contenido;
}

Esto genera:

public Mensaje() {
}

Si tenemos campos finales, necesitamos configurar la anotación:

import lombok.NoArgsConstructor;
import lombok.AccessLevel;

@NoArgsConstructor(force = true)
public class Documento {
    private final String id; // Se inicializará con null
    private String titulo;
}

El parámetro force = true inicializará los campos finales con valores predeterminados (null, 0, false).

@AllArgsConstructor

Genera un constructor con todos los campos como parámetros:

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class Punto {
    private int x;
    private int y;
}

Esto genera:

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

@RequiredArgsConstructor

Genera un constructor que incluye solo los campos que son finales o están marcados con @NonNull:

import lombok.RequiredArgsConstructor;
import lombok.NonNull;

@RequiredArgsConstructor
public class Servicio {
    private final String nombre;
    @NonNull private String descripcion;
    private double precio; // No incluido en el constructor
}

Esto genera:

public Servicio(String nombre, @NonNull String descripcion) {
    if (descripcion == null) {
        throw new NullPointerException("descripcion is marked non-null but is null");
    }
    this.nombre = nombre;
    this.descripcion = descripcion;
}

La anotación @NonNull no solo marca el campo para incluirlo en el constructor, sino que también genera una validación de nulidad en tiempo de ejecución.

Personalización de constructores

Podemos personalizar los constructores generados:

import lombok.AllArgsConstructor;
import lombok.AccessLevel;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Configuracion {
    private String host;
    private int puerto;
}

Esto genera un constructor protegido en lugar de público.

Combinando anotaciones de constructor

Es posible combinar varias anotaciones de constructor:

import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
public class Producto {
    private String codigo;
    private String nombre;
    private double precio;
}

Esto generará dos constructores: uno sin argumentos y otro con todos los campos.

Anotación @Builder

Aunque no es estrictamente una anotación de constructor, @Builder merece mención ya que proporciona una forma elegante de construir objetos:

import lombok.Builder;

@Builder
public class Email {
    private String destinatario;
    private String remitente;
    private String asunto;
    private String cuerpo;
    private boolean urgente;
}

Esto permite crear instancias utilizando el patrón Builder:

Email email = Email.builder()
    .destinatario("usuario@ejemplo.com")
    .remitente("sistema@empresa.com")
    .asunto("Notificación importante")
    .cuerpo("Este es un mensaje automático.")
    .urgente(true)
    .build();

Casos de uso prácticos

Entidades JPA

Las anotaciones de Lombok son especialmente útiles con entidades JPA:

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.persistence.*;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Cliente {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    private String email;
    
    @OneToMany(mappedBy = "cliente", cascade = CascadeType.ALL)
    private List<Pedido> pedidos;
}

DTOs con validación

Combinando Lombok con validaciones de Bean Validation:

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import javax.validation.constraints.*;

@Getter
@Setter
@NoArgsConstructor
public class UsuarioDTO {
    @NotNull
    private Long id;
    
    @NotBlank
    @Size(min = 3, max = 50)
    private String nombre;
    
    @Email
    @NotBlank
    private String email;
    
    @Pattern(regexp = "^\\d{9}$")
    private String telefono;
}

Objetos inmutables

Para crear objetos inmutables:

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class Configuracion {
    private final String entorno;
    private final String apiKey;
    private final int timeout;
    private final boolean debug;
}

Consideraciones importantes

  • Encapsulación: Aunque es conveniente generar getters y setters para todos los campos, considera si realmente todos los campos deben ser accesibles o modificables.
  • Inmutabilidad: Para clases inmutables, usa @Getter con campos finales y evita @Setter.
  • Validación: Los setters generados por Lombok no incluyen validación. Si necesitas validar datos, considera escribir setters personalizados.
  • Herencia: Ten cuidado al usar estas anotaciones en jerarquías de clases, especialmente con constructores.

Estas anotaciones específicas de Lombok ofrecen un control más preciso sobre la generación de código que @Data, permitiéndote adaptar exactamente qué funcionalidades necesitas para cada clase según sus requisitos particulares.

Aprende Java online

Otras lecciones de Java

Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Java y certifícate

Ejercicios de programación de Java

Evalúa tus conocimientos de esta lección Lombok para Java con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Certificados de superación de Java

Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.