java-idioms
SkillAI & modelsJava 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the java-idioms skill
About this capability
Comprehensive sets of standards and practices designed to elevate the capabilities of AI coding agents.
What this skill tells your AI
The instructions your AI receives, as published by irahardianto/awesome-agv in .agents/skills/java-idioms/SKILL.md and read by ahel’s review.
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: .agents/rules/testing-strategy.md. Logging:
@.agents/skills/logging-implementation/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
Exceptionbroadly — catch specific exceptions. Never swallow exceptions silently. -
Optionalfor 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
@Autowiredfield 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.
TaskServicenotITaskService.userIdnotstrUserId. - Package names: lowercase, no underscores.
com.example.tasknotcom.example.task_management.
Testing
Test naming, pyramid: .agents/rules/testing-strategy.md. Java-specific tooling below.
-
JUnit 5 + AssertJ:
@Test void calculateDiscount_returnsZero_whenNoItems() { var result = calculator.calculateDiscount(List.of(), coupon); assertThat(result).isEqualTo(0.0); } -
@ParameterizedTestfor 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 .agents/rules/code-idioms-and-conventions.md.
| 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 .agents/rules/code-idioms-and-conventions.md
- Testing Strategy .agents/rules/testing-strategy.md
- Error Handling Principles .agents/rules/error-handling-principles.md
- Dependency Management Principles @.agents/rules/dependency-management-principles.md
- Logging Implementation @.agents/skills/logging-implementation/SKILL.md
Signals
- GitHub stars
- 156
- Forks
- 53
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
java-idioms- Source
- github.com/irahardianto/awesome-agv