--- 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