Java Idioms and Patterns
Java rewards clarity, type safety, and robust ecosystem tooling. Modern Java (17+ LTS) favors records, sealed classes, and pattern matching. Idiomatic Java = clean, readable, framework-aware.
Scope: Java coding idioms. Test naming: GEMINI.md § Testing Strategy. Logging: @.gemini/skills/logging-and-observability-principles/SKILL.md.
Modern Java Features (17+ LTS)
Records for immutable data carriers:
// ✅ Concise, immutable, auto-generated equals/hashCode/toString
public record CreateTaskRequest(String title, Priority priority) {}
// ❌ Verbose boilerplate POJO
public class CreateTaskRequest { /* getters, setters, equals, hashCode... */ }
Sealed classes for constrained hierarchies:
public sealed interface TaskResult permits Success, Failure, Pending {}
public record Success(Task task) implements TaskResult {}
public record Failure(String reason) implements TaskResult {}
public record Pending(String taskId) implements TaskResult {}
Pattern matching with switch:
return switch (result) {
case Success(var task) -> ResponseEntity.ok(task);
case Failure(var reason) -> ResponseEntity.badRequest().body(reason);
case Pending(var id) -> ResponseEntity.accepted().body(id);
};
Text blocks for queries and templates:
String query = """
SELECT t.id, t.title, t.priority
FROM tasks t
WHERE t.user_id = ?
ORDER BY t.created_at DESC
""";
Virtual threads (21+) for I/O-bound work:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> fetchUser(userId));
executor.submit(() -> fetchTasks(userId));
}
Error Handling
Domain exception hierarchies — never raw Exception:
public abstract class DomainException extends RuntimeException {
protected DomainException(String message) { super(message); }
}
public class NotFoundException extends DomainException {
private final String resource;
private final String resourceId;
public NotFoundException(String resource, String resourceId) {
super(String.format("%s '%s' not found", resource, resourceId));
this.resource = resource;
this.resourceId = resourceId;
}
}
Never catch Exception broadly — catch specific exceptions. Never swallow exceptions silently.
Optional for nullable returns — never for parameters:
// ✅ Return type
public Optional<Task> findById(String id) { ... }
// ❌ Parameter — use overloading or @Nullable instead
public void process(Optional<String> filter) { ... }
Interfaces and DI
Program to interfaces, inject via constructor:
// ✅ Interface in consumer package
public interface TaskStorage {
Task getById(String id);
void save(Task task);
}
// ✅ Constructor injection (Spring auto-wires)
@Service
public class TaskService {
private final TaskStorage storage;
public TaskService(TaskStorage storage) { this.storage = storage; }
}
Prefer constructor injection over @Autowired field injection. No field injection — ever.
Naming
- PascalCase for classes, interfaces, enums, records.
- camelCase for methods, fields, local variables.
- UPPER_SNAKE_CASE for constants (
static final).
- No Hungarian notation.
TaskService not ITaskService. userId not strUserId.
- Package names: lowercase, no underscores.
com.example.task not com.example.task_management.
Testing
Test naming, pyramid: GEMINI.md § Testing Strategy. Java-specific tooling below.
JUnit 5 + AssertJ:
@Test
void calculateDiscount_returnsZero_whenNoItems() {
var result = calculator.calculateDiscount(List.of(), coupon);
assertThat(result).isEqualTo(0.0);
}
@ParameterizedTest for table-driven tests:
@ParameterizedTest
@CsvSource({"low,1", "medium,5", "high,10"})
void priorityScore_mapsCorrectly(String priority, int expected) {
assertThat(Priority.score(priority)).isEqualTo(expected);
}
Mockito for mocking — never PowerMock:
@ExtendWith(MockitoExtension.class)
class TaskServiceTest {
@Mock TaskStorage storage;
@InjectMocks TaskService service;
}
TestContainers for integration tests — real DB, no in-memory substitutes for critical paths.
Formatting and Static Analysis
Must pass zero warnings/errors before commit. See GEMINI.md § Code Completion Mandate.
| Tool |
Purpose |
Command |
google-java-format |
Canonical formatting |
google-java-format --replace src/**/*.java |
SpotBugs |
Bug detection |
mvn spotbugs:check or gradle spotbugsMain |
Error Prone |
Compile-time bug detection |
Compiler plugin |
Checkstyle |
Style enforcement |
mvn checkstyle:check |
SonarQube |
Comprehensive analysis |
CI integration |
OWASP Dependency-Check |
CVE scanning |
mvn dependency-check:check |
Related
- Code Idioms and Conventions GEMINI.md § Code Idioms and Conventions
- Testing Strategy GEMINI.md § Testing Strategy
- Error Handling Principles GEMINI.md § Error Handling Principles
- Dependency Management Principles @.gemini/skills/dependency-management-principles/SKILL.md
- Logging and Observability Principles @.gemini/skills/logging-and-observability-principles/SKILL.md
1---2name: java-idioms3description: Java Idioms and Patterns4---56## Java Idioms and Patterns78Java rewards clarity, type safety, and robust ecosystem tooling. Modern Java (17+ LTS) favors records, sealed classes, and pattern matching. Idiomatic Java = clean, readable, framework-aware.910> Scope: Java coding idioms. Test naming: GEMINI.md § Testing Strategy. Logging: `@.gemini/skills/logging-and-observability-principles/SKILL.md`.1112### Modern Java Features (17+ LTS)13141. **Records for immutable data carriers:**15 ```java16 // ✅ Concise, immutable, auto-generated equals/hashCode/toString17 public record CreateTaskRequest(String title, Priority priority) {}1819 // ❌ Verbose boilerplate POJO20 public class CreateTaskRequest { /* getters, setters, equals, hashCode... */ }21 ```22232. **Sealed classes for constrained hierarchies:**24 ```java25 public sealed interface TaskResult permits Success, Failure, Pending {}26 public record Success(Task task) implements TaskResult {}27 public record Failure(String reason) implements TaskResult {}28 public record Pending(String taskId) implements TaskResult {}29 ```30313. **Pattern matching with `switch`:**32 ```java33 return switch (result) {34 case Success(var task) -> ResponseEntity.ok(task);35 case Failure(var reason) -> ResponseEntity.badRequest().body(reason);36 case Pending(var id) -> ResponseEntity.accepted().body(id);37 };38 ```39404. **Text blocks for queries and templates:**41 ```java42 String query = """43 SELECT t.id, t.title, t.priority44 FROM tasks t45 WHERE t.user_id = ?46 ORDER BY t.created_at DESC47 """;48 ```49505. **Virtual threads (21+) for I/O-bound work:**51 ```java52 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {53 executor.submit(() -> fetchUser(userId));54 executor.submit(() -> fetchTasks(userId));55 }56 ```5758### Error Handling59601. **Domain exception hierarchies — never raw `Exception`:**61 ```java62 public abstract class DomainException extends RuntimeException {63 protected DomainException(String message) { super(message); }64 }6566 public class NotFoundException extends DomainException {67 private final String resource;68 private final String resourceId;69 public NotFoundException(String resource, String resourceId) {70 super(String.format("%s '%s' not found", resource, resourceId));71 this.resource = resource;72 this.resourceId = resourceId;73 }74 }75 ```76772. **Never catch `Exception` broadly** — catch specific exceptions. Never swallow exceptions silently.78793. **`Optional` for nullable returns — never for parameters:**80 ```java81 // ✅ Return type82 public Optional<Task> findById(String id) { ... }8384 // ❌ Parameter — use overloading or @Nullable instead85 public void process(Optional<String> filter) { ... }86 ```8788### Interfaces and DI89901. **Program to interfaces, inject via constructor:**91 ```java92 // ✅ Interface in consumer package93 public interface TaskStorage {94 Task getById(String id);95 void save(Task task);96 }9798 // ✅ Constructor injection (Spring auto-wires)99 @Service100 public class TaskService {101 private final TaskStorage storage;102 public TaskService(TaskStorage storage) { this.storage = storage; }103 }104 ```1051062. **Prefer constructor injection over `@Autowired` field injection.** No field injection — ever.107108### Naming1091101. **PascalCase** for classes, interfaces, enums, records.1112. **camelCase** for methods, fields, local variables.1123. **UPPER_SNAKE_CASE** for constants (`static final`).1134. **No Hungarian notation.** `TaskService` not `ITaskService`. `userId` not `strUserId`.1145. **Package names**: lowercase, no underscores. `com.example.task` not `com.example.task_management`.115116### Testing117118> Test naming, pyramid: GEMINI.md § Testing Strategy. Java-specific tooling below.1191201. **JUnit 5 + AssertJ:**121 ```java122 @Test123 void calculateDiscount_returnsZero_whenNoItems() {124 var result = calculator.calculateDiscount(List.of(), coupon);125 assertThat(result).isEqualTo(0.0);126 }127 ```1281292. **`@ParameterizedTest` for table-driven tests:**130 ```java131 @ParameterizedTest132 @CsvSource({"low,1", "medium,5", "high,10"})133 void priorityScore_mapsCorrectly(String priority, int expected) {134 assertThat(Priority.score(priority)).isEqualTo(expected);135 }136 ```1371383. **Mockito for mocking — never PowerMock:**139 ```java140 @ExtendWith(MockitoExtension.class)141 class TaskServiceTest {142 @Mock TaskStorage storage;143 @InjectMocks TaskService service;144 }145 ```1461474. **TestContainers for integration tests** — real DB, no in-memory substitutes for critical paths.148149### Formatting and Static Analysis150151Must pass zero warnings/errors before commit. See GEMINI.md § Code Completion Mandate.152153| Tool | Purpose | Command |154|---|---|---|155| `google-java-format` | Canonical formatting | `google-java-format --replace src/**/*.java` |156| `SpotBugs` | Bug detection | `mvn spotbugs:check` or `gradle spotbugsMain` |157| `Error Prone` | Compile-time bug detection | Compiler plugin |158| `Checkstyle` | Style enforcement | `mvn checkstyle:check` |159| `SonarQube` | Comprehensive analysis | CI integration |160| `OWASP Dependency-Check` | CVE scanning | `mvn dependency-check:check` |161162### Related163- Code Idioms and Conventions GEMINI.md § Code Idioms and Conventions164- Testing Strategy GEMINI.md § Testing Strategy165- Error Handling Principles GEMINI.md § Error Handling Principles166- Dependency Management Principles @.gemini/skills/dependency-management-principles/SKILL.md167- Logging and Observability Principles @.gemini/skills/logging-and-observability-principles/SKILL.md