Introduction
Java is a powerful and mature language, but it comes with a cost: a lot of boilerplate code.
Think about the endless getters, setters, constructors, and toString() methods you’ve written over the years. They don’t add business value — they just make your code longer and harder to read.
That’s where Project Lombok comes to the rescue. Lombok uses annotations to generate boilerplate code at compile time, letting you focus on actual logic instead of repetitive syntax.
It integrates perfectly with Spring Boot, which makes it one of the most popular tools in modern Java development.
In this article, we’ll explore 10 Lombok annotations every Java developer should know and discuss best practices and common pitfalls so you can use Lombok effectively and safely.
How to Add Lombok to Your Project
Before you start using Lombok, make sure your project and IDE are properly configured.
Maven
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
Gradle
compileOnly 'org.projectlombok:lombok:1.18.32'
annotationProcessor 'org.projectlombok:lombok:1.18.32'
IDE Configuration
IntelliJ IDEA →
Settings > Build, Execution, Deployment > Compiler > Annotation Processors > Enable annotation processingEclipse →
Preferences > Java Compiler > Annotation Processing > Enable-
VS Code →
- Make sure you have the Extension Pack for Java installed (includes the Language Support for Java by Red Hat).
- Open your workspace settings and add:
"java.configuration.annotationProcessing.enabled": true - Restart VS Code and recompile the project.
Once configured, Lombok’s annotations will work seamlessly in your IDE, and you’re ready to say goodbye to boilerplate.
The 10 Most Useful Lombok Annotations
Here are the top annotations that will make your Java code cleaner, shorter, and more readable.
1. @Getter / @Setter
These annotations generate standard getter and setter methods automatically.
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter @Setter
private String name;
@Getter
private int age;
}
At class level:
@Getter
@Setter
public class User {
private String name;
private int age;
}
✅ Why is it useful?
- Removes repetitive method definitions.
- Keeps your classes focused on business logic.
- You can control visibility:
@Getter(AccessLevel.PROTECTED)for example.
2. @Data
Combines several Lombok annotations into one:
-
@Getter/@Setter -
@ToString -
@EqualsAndHashCode -
@RequiredArgsConstructor
import lombok.Data;
@Data
public class Product {
private final String id;
private String name;
}
✅ Why is it useful?
- Great for simple DTOs and data containers.
- Saves multiple lines of boilerplate.
⚠ When to avoid it:
- Not ideal for JPA entities or domain models where you need more control over equality and mutability.
3. @Builder
Implements the Builder pattern, letting you create objects in a more expressive and flexible way.
import lombok.Builder;
@Builder
public class User {
private String name;
private int age;
}
// Usage
User user = User.builder()
.name("Alice")
.age(25)
.build();
✅ Why is it useful?
Eliminates complex constructors with many parameters.
Improves code readability.
Makes object creation safe and expressive (especially in tests).
4. @AllArgsConstructor / @NoArgsConstructor
Generates constructors with all fields or no fields respectively.
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}
✅ Why is it useful?
- Perfect for frameworks like JPA or Jackson, which require a no-args constructor.
- Speeds up development when dealing with DTOs or entity creation.
5. @RequiredArgsConstructor
Generates a constructor for all final fields (and those marked with @NonNull).
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Service {
private final Repository repository;
}
In Spring Boot, this shines with constructor injection:
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
}
✅ Why is it useful?
- Encourages dependency injection via constructors.
- No need for
@Autowired. - Promotes immutability and cleaner design.
6. @FieldDefaults (and Advanced Combo)
@FieldDefaults lets you define default modifiers for fields across a class, reducing repetitive access modifiers.
It has two main properties:
-
level→ sets default access level (PRIVATE,PROTECTED,PUBLIC). -
makeFinal→ makes all fieldsfinaliftrue.
Example 1: Set default access level
import lombok.experimental.FieldDefaults;
import static lombok.AccessLevel.PRIVATE;
@FieldDefaults(level = PRIVATE)
public class UserService {
String username;
String email;
}
Example 2: Combine with makeFinal for immutability
import lombok.experimental.FieldDefaults;
import lombok.RequiredArgsConstructor;
import static lombok.AccessLevel.PRIVATE;
@FieldDefaults(level = PRIVATE, makeFinal = true)
@RequiredArgsConstructor
public class UserService {
UserRepository userRepository;
NotificationService notificationService;
}
✅ Why is it useful?
- Keeps field definitions consistent and clean.
- Reduces risk of accidentally exposing internal state.
- Perfect for constructor injection when combined with
@RequiredArgsConstructor.
💡 Tip: You don’t always need both properties.
Use only level for access control, or makeFinal when enforcing immutability.
7. @Value
@Value is like @Data, but it creates immutable classes:
- All fields are
private final. - No setters are generated.
import lombok.Value;
@Value
public class Address {
String city;
String zip;
}
✅ Why is it useful?
- Promotes immutability and thread-safety.
- Excellent for DTOs and value objects.
- Works perfectly with the Builder pattern.
8. @Slf4j
Generates an SLF4J logger automatically.
Without Lombok:
private static final Logger log = LoggerFactory.getLogger(MyService.class);
With Lombok:
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderService {
public void processOrder(String orderId) {
log.info("Processing order: {}", orderId);
try {
// some business logic
} catch (Exception e) {
log.error("Error processing order {}", orderId, e);
}
}
}
✅ Why is it useful?
- No more manual logger setup.
- Fully compatible with Spring Boot’s default logging (SLF4J + Logback).
- Supports structured logging and all log levels (
info,warn,error,debug). - Keeps code cleaner and easier to maintain.
Other variants available:
@Log, @Log4j, @Log4j2, @CommonsLog — but @Slf4j is the most common and recommended.
9. @UtilityClass
@UtilityClass is a hidden gem in Lombok’s toolkit.
It’s used to create utility classes — classes that only contain static methods and constants.
Lombok will automatically:
- Make the class
final. - Add a private constructor (preventing instantiation).
- Convert all fields and methods to static.
import lombok.experimental.UtilityClass;
@UtilityClass
public class MathUtils {
public int add(int a, int b) {
return a + b;
}
public final double PI = 3.14159;
}
Usage:
int sum = MathUtils.add(5, 10);
✅ Why is it useful?
- Eliminates boilerplate for utility classes.
- Enforces correct usage (non-instantiable, static members).
- Perfect for helpers, validators, or constants.
Without Lombok, you’d need to write:
public final class MathUtils {
private MathUtils() {}
public static int add(int a, int b) {
return a + b;
}
}
With Lombok — one line. Clean and elegant.
10. @ToString
Generates a toString() method automatically.
import lombok.ToString;
@ToString
public class User {
private String name;
private int age;
}
Exclude fields when needed:
@ToString(exclude = "password")
public class User {
private String username;
private String password;
}
✅ Why is it useful?
- Great for debugging and logging.
- Avoids maintenance issues when adding or renaming fields.
- Works perfectly with
@Dataor on its own for better control.
Best Practices & When to Avoid Lombok
While Lombok can make your life easier, it’s not always the right tool for every situation.
- Avoid overusing
@DataIt may generate methods you don’t need and cause issues in entities or complex models. - Prefer immutability
Use
@ValueormakeFinal = truein@FieldDefaultswhen possible. - Mind your team’s tooling Ensure everyone’s IDE and CI/CD pipelines support annotation processing.
- Be explicit when needed Don’t hide complexity behind annotations if it reduces clarity for new contributors.
Conclusion
Lombok is a must-have library for Java developers who value clean, maintainable, and expressive code.
It removes the friction of boilerplate, integrates seamlessly with Spring Boot, and helps you focus on what truly matters: business logic.
Key takeaways:
- Use Lombok for faster development and cleaner classes.
- Combine
@FieldDefaultswith@RequiredArgsConstructorfor elegant constructor injection. - Use
@Valuefor immutability and@Builderfor readability. - Don’t overlook gems like
@Slf4jand@UtilityClass.
✅ Want more? Check out the GitHub repository for more examples and full implementations and my Pro Starter Kit on Gumroad!
💬 Join the Discussion
What do you think about Lombok? Do you love it, or do you find it hides too much magic?
Share your thoughts in the comments!
- 👉 How has Lombok helped (or hurt) your Java projects?
- 👉 Which annotations do you use the most?
Your feedback will help other developers (and me!) improve future articles.
Top comments (0)