Files

6.3 KiB

Lombok Conversion Guide

When This Applies

Detected when imports match lombok.*.

Core Rule

Remove ALL Lombok annotations entirely. Do not convert Lombok to Lombok — convert to idiomatic Kotlin equivalents. Lombok has no place in Kotlin code.

Annotation Conversion Table

Lombok Annotation Kotlin Equivalent
@Getter / @Setter Kotlin properties (val/var) — automatic
@Data data class with primary constructor properties
@Value (Lombok) data class with val properties (immutable)
@Builder Default parameter values, or named arguments. For complex builders, use Kotlin builder DSL
@NoArgsConstructor Secondary no-arg constructor, or default values for all params
@AllArgsConstructor Primary constructor (Kotlin default)
@RequiredArgsConstructor Primary constructor with only required (non-default) params
@ToString data class auto-generates toString, or manual override fun toString()
@EqualsAndHashCode data class auto-generates, or manual override fun equals/hashCode
@Slf4j / @Log / @Log4j2 Companion object with logger (see example below)
@Cleanup Kotlin's .use {} extension function
@SneakyThrows Kotlin has no checked exceptions — just remove it
@Synchronized Kotlin's @Synchronized annotation
@With data class .copy() method
@Accessors(chain = true) Kotlin's apply {} block

Key Rules

  1. @Slf4j — Convert to a companion object with an explicit logger:
companion object {
    private val log = LoggerFactory.getLogger(MyClass::class.java)
}
  1. @Data with JPA entities — Do NOT use data class for JPA entities. Use regular open class with properties instead. Data classes break Hibernate proxies.

  2. @Builder — Prefer default parameter values. Only create an explicit builder pattern if the Java code has complex builder logic beyond simple setters.

  3. Lombok val — Replace with Kotlin's val (they serve the same purpose).


Example 1: @Data Class with @Builder

Java Input

package com.acme.model;

import lombok.Builder;
import lombok.Data;

/**
 * Represents a customer order with shipping details.
 */
@Data
@Builder
public class Order {
    private String orderId;
    private String customerName;
    private int quantity;
    private boolean expedited;
}

Kotlin Output

package com.acme.model

/**
 * Represents a customer order with shipping details.
 */
data class Order(
    val orderId: String?,
    val customerName: String?,
    val quantity: Int = 0,
    val expedited: Boolean = false
)

What changed:

  • @Datadata class with primary constructor properties.
  • @Builder → default parameter values. Callers use named arguments: Order(orderId = "123", customerName = "Alice", quantity = 2).
  • All Lombok imports removed.
  • Fields become val properties (immutable by default; use var only if mutation is required by the original code).
  • Reference types are nullable (String?) because Java fields default to null unless proven otherwise.

Example 2: @Slf4j Annotated Service Class

Java Input

package com.acme.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Service that processes incoming payment requests.
 */
@Slf4j
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentGateway gateway;
    private final NotificationSender notifier;

    /**
     * Processes a payment for the given amount.
     *
     * @param amount the payment amount in cents
     * @return true if the payment succeeded
     */
    public boolean processPayment(long amount) {
        log.info("Processing payment of {} cents", amount);
        try {
            gateway.charge(amount);
            notifier.sendConfirmation(amount);
            log.info("Payment of {} cents succeeded", amount);
            return true;
        } catch (Exception e) {
            log.error("Payment failed for amount {}", amount, e);
            return false;
        }
    }
}

Kotlin Output

package com.acme.service

import org.slf4j.LoggerFactory

/**
 * Service that processes incoming payment requests.
 */
open class PaymentService(
    private val gateway: PaymentGateway,
    private val notifier: NotificationSender
) {

    companion object {
        private val log = LoggerFactory.getLogger(PaymentService::class.java)
    }

    /**
     * Processes a payment for the given amount.
     *
     * @param amount the payment amount in cents
     * @return true if the payment succeeded
     */
    fun processPayment(amount: Long): Boolean {
        log.info("Processing payment of {} cents", amount)
        return try {
            gateway.charge(amount)
            notifier.sendConfirmation(amount)
            log.info("Payment of {} cents succeeded", amount)
            true
        } catch (e: Exception) {
            log.error("Payment failed for amount {}", amount, e)
            false
        }
    }
}

What changed:

  • @Slf4j → companion object with LoggerFactory.getLogger(...).
  • @RequiredArgsConstructor → primary constructor with val parameters.
  • Lombok imports replaced with org.slf4j.LoggerFactory.
  • try/catch used as an expression (idiomatic Kotlin).
  • Class is open because Java classes are implicitly open.

Example 3: @Value (Lombok) Immutable Class

Java Input

package com.acme.config;

import lombok.Value;

/**
 * Immutable configuration for connecting to a database.
 */
@Value
public class DatabaseConfig {
    String host;
    int port;
    String databaseName;
    boolean useSsl;
}

Kotlin Output

package com.acme.config

/**
 * Immutable configuration for connecting to a database.
 */
data class DatabaseConfig(
    val host: String?,
    val port: Int,
    val databaseName: String?,
    val useSsl: Boolean
)

What changed:

  • @Valuedata class with val properties (all immutable).
  • Lombok's @Value makes the class final, and Kotlin data class is also final by default — so the semantics match.
  • All Lombok imports removed.
  • Auto-generated equals(), hashCode(), toString(), and copy() come from data class for free.
  • Reference types are nullable (String?) since the original Java fields have no nullability annotations.