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
- @Slf4j — Convert to a companion object with an explicit logger:
companion object {
private val log = LoggerFactory.getLogger(MyClass::class.java)
}@Data with JPA entities — Do NOT use
data classfor JPA entities. Use regularopen classwith properties instead. Data classes break Hibernate proxies.@Builder — Prefer default parameter values. Only create an explicit builder pattern if the Java code has complex builder logic beyond simple setters.
Lombok
val— Replace with Kotlin'sval(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:
@Data→data classwith primary constructor properties.@Builder→ default parameter values. Callers use named arguments:Order(orderId = "123", customerName = "Alice", quantity = 2).- All Lombok imports removed.
- Fields become
valproperties (immutable by default; usevaronly if mutation is required by the original code). - Reference types are nullable (
String?) because Java fields default tonullunless 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 withLoggerFactory.getLogger(...).@RequiredArgsConstructor→ primary constructor withvalparameters.- Lombok imports replaced with
org.slf4j.LoggerFactory. try/catchused as an expression (idiomatic Kotlin).- Class is
openbecause 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:
@Value→data classwithvalproperties (all immutable).- Lombok's
@Valuemakes the class final, and Kotlindata classis also final by default — so the semantics match. - All Lombok imports removed.
- Auto-generated
equals(),hashCode(),toString(), andcopy()come fromdata classfor free. - Reference types are nullable (
String?) since the original Java fields have no nullability annotations.