6.3 KiB
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
- @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.