---
name: java-lombok
description: Lombok patterns including @Delegate, @Builder, @Value, @UtilityClass for reducing boilerplate
user-invocable: false
allowed-tools: Read, Edit, Write, Bash, Grep, Glob
---
# Java Lombok Skill
Lombok standards for reducing boilerplate code while maintaining code quality and testability.
## Prerequisites
```xml
org.projectlombok
lombok
provided
```
## Required Imports
```java
// Lombok Core
import lombok.Builder;
import lombok.Value;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
// Lombok Advanced
import lombok.Delegate;
import lombok.Singular;
import lombok.experimental.UtilityClass;
import lombok.Builder.Default;
```
## Core Annotations
### @Delegate - Delegation Over Inheritance
Use `@Delegate` for delegation patterns instead of inheritance:
```java
// CORRECT - delegation with Lombok
public class CachedTokenValidator implements TokenValidator {
@Delegate
private final TokenValidator delegate;
private final Cache cache;
public CachedTokenValidator(TokenValidator delegate) {
this.delegate = delegate;
this.cache = CacheBuilder.newBuilder().build();
}
@Override
public ValidationResult validate(String token) {
return cache.get(token, () -> delegate.validate(token));
}
}
// WRONG - inheritance creates tight coupling
public class CachedTokenValidator extends BaseTokenValidator { }
```
**Use @Delegate for**: Interface composition, wrapping implementations, cross-cutting concerns (caching, logging, metrics), avoiding inheritance hierarchies.
### @Builder - Complex Object Construction
Use `@Builder` for classes with multiple optional parameters:
```java
@Value
@Builder(toBuilder = true)
public class TokenConfig {
String issuer;
String audience;
@Builder.Default
Duration validity = Duration.ofHours(1);
@Builder.Default
int clockSkewSeconds = 30;
@Singular
Set requiredClaims;
}
// Usage
TokenConfig config = TokenConfig.builder()
.issuer("https://auth.example.com")
.audience("my-api")
.requiredClaim("sub") // @Singular generates add method
.requiredClaim("exp")
.build();
// Copy with modifications via toBuilder()
TokenConfig modified = config.toBuilder()
.validity(Duration.ofHours(2))
.build();
```
**Use @Builder for**: Classes with 3+ parameters, optional parameters, immutable configuration objects, DTOs with many fields.
### @Value - Immutable Objects
Use `@Value` for immutable value objects and DTOs:
```java
@Value
public class ValidationResult {
boolean valid;
List errors;
Instant validatedAt;
}
// Usage
ValidationResult result = new ValidationResult(true, List.of(), Instant.now());
boolean isValid = result.isValid(); // Getter
```
`@Value` generates: all-args constructor, getters (no setters), equals/hashCode, toString, all fields private final.
**Use @Value for**: Immutable DTOs, value objects, API request/response objects, configuration data.
### @Data - Mutable Objects (Use Sparingly)
```java
@Data
public class UserPreferences {
private String theme;
private Locale locale;
private int pageSize;
}
```
**Prefer @Value or records for immutability**. Use @Data only when mutability is genuinely required.
### @UtilityClass - Static Method Classes
```java
@UtilityClass
public class TokenUtils {
public static String extractTokenId(String token) {
// Implementation
}
public static boolean isExpired(String token) {
// Implementation
}
}
```
Makes class final, constructor private, all methods static.
## Records vs Lombok @Value
| Criteria | Use Records | Use Lombok @Value |
|----------|-------------|-------------------|
| Java version | Java 17+ | Java 11+ |
| Builder pattern | Not built-in | @Value + @Builder |
| Collection builders | Not available | @Singular |
| Pattern matching | Java 21+ | Not available |
| Project context | Minimal dependencies | Already using Lombok |
| Customization | Limited | More flexible |
```java
// Simple case - prefer records (Java 17+)
public record User(String id, String name, String email) {}
// Complex case - use Lombok
@Value
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiResponse {
@JsonProperty("user_id")
String userId;
String status;
@Singular
List messages;
}
```
**Migration guidance**: See `pm-dev-java:java-core` skill for Lombok to records migration.
## Combining Annotations
```java
@Value
@Builder
public class SearchCriteria {
String query;
@Builder.Default
int maxResults = 100;
@Singular
Set categories;
LocalDate startDate;
LocalDate endDate;
}
// Usage - only specify what differs from defaults
SearchCriteria criteria = SearchCriteria.builder()
.query("example")
.category("tech")
.category("java")
.build();
```
## Logging: @Slf4j Prohibition
**Do NOT use `@Slf4j`** or similar logging annotations in CUI projects:
```java
// WRONG - Do not use in CUI projects
@Slf4j
public class TokenValidator { }
// CORRECT - Use CuiLogger explicitly
public class TokenValidator {
private static final CuiLogger LOGGER = new CuiLogger(TokenValidator.class);
}
```
CUI projects use `CuiLogger`, not SLF4J. See `pm-dev-java-cui:cui-logging` for details.
## Common Pitfalls
| Pitfall | Wrong | Correct |
|---------|-------|---------|
| Overusing @Data | `@Data` for immutable objects | Use `@Value` |
| Missing defaults | Builder without `@Builder.Default` | Add defaults for optional fields |
| Wrong logger | `@Slf4j` in CUI projects | Use `CuiLogger` explicitly |
| No toBuilder | Immutable without copy method | `@Builder(toBuilder = true)` |
| Inheritance | `extends BaseClass` | `@Delegate` with composition |
## Best Practices Summary
1. **Prefer immutability**: Use `@Value` over `@Data`
2. **Use @Builder for 3+ parameters**: Avoid long constructors
3. **Provide @Builder.Default**: For optional fields with sensible defaults
4. **Use @Singular for collections**: Cleaner builder API
5. **Use @Delegate for composition**: Avoid inheritance hierarchies
6. **Consider records (Java 17+)**: For simple data carriers
7. **Use @UtilityClass**: For static-only classes
## Quality Checklist
- [ ] @Value used for immutable objects
- [ ] @Builder used for classes with 3+ parameters
- [ ] @Delegate used instead of inheritance
- [ ] @Builder.Default provided for optional fields
- [ ] @Singular used for collection builders
- [ ] Records considered as alternative (Java 17+)
- [ ] No Lombok logging annotations (use CuiLogger)
- [ ] @Data used only when mutability required
- [ ] @UtilityClass used for utility classes
## Related Skills
- `pm-dev-java:java-core` - Core Java patterns, records migration
- `pm-dev-java:java-null-safety` - Null safety with Lombok