1---2name: java-conventions3description: Generic, composable Java 25 code conventions — modern syntax, code style, naming, visibility, structure, methods, streams, exceptions, and documentation rules that apply across all Java contexts (single-file scripts, CLI apps, MicroProfile/Jakarta EE servers, libraries). Technology-neutral within the Java world; meant to be composed with context-specific skills (e.g. `java-cli-script`, `java-cli-app`, `microprofile-server`, `bce`). Use when writing, generating, or reviewing Java code anywhere the composed skill does not already specify style. Triggers on "Java conventions", "Java style", "Java code style", "modern Java", "Java 25", "idiomatic Java", or any request to write or review Java code where context-specific skills do not already cover style.4---56## Scope78- Language-level rules for Java 25 only — syntax, style, naming, visibility, structure, methods, streams, exceptions, comments.9- Architecture (BCE, layering, packaging) is **not** in this skill — see `bce`.10- Build, packaging, framework, and protocol rules are **not** in this skill — see `java-cli-script`, `java-cli-app`, `microprofile-server`, `zb`, etc.11- When a composed skill specifies a rule, the composed skill wins; this skill is the fallback baseline.1213## Java Version & Syntax1415- target Java 25; assume all features are GA — never use `--enable-preview`16- use modern syntax naturally: `var`, records, sealed types, pattern matching, text blocks, switch expressions17- use `var` for local variable declarations where the type is obvious from the right-hand side18- use module imports (e.g. `import module java.net.http;`) over individual type imports19- do not import packages from `java.base` — it is automatically available20- use switch expressions with arrow syntax (`case X -> ...`) over old `case X:` statements with `break`21- use pattern matching for `instanceof` — `if (o instanceof String s)` over cast-and-assign22- use pattern matching in `switch` for type-based dispatch23- use the diamond operator `<>` for generic type inference24- never use raw generic types — always parameterize25- prefer `void main()` / `void main(String... args)` over `public static void main(String[] args)`; instance main, not static2627## Java SE APIs2829- use Java SE APIs over writing custom code — the standard library has it, usually in `java.util`, `java.nio.file`, `java.net.http`, or `java.time`30- prefer the most specific Java SE type for the domain — `Path` over `String`, `URI` over `String`, `Duration` over `long millis`, `Instant`/`LocalDate` over `Date`/`long`3132## Visibility & Modifiers3334- avoid `private` methods (including `private static` helpers) — prefer package-private (default) so same-package unit tests can exercise them directly, including edge cases, without round-tripping through the public API35- avoid `private` fields — prefer package-private so same-package tests can read or seed state without reflection or extra accessors36- reserve `private` for genuinely sensitive state (credentials, security tokens, invariants that must never be observed externally); the burden of justification is on `private`, not on package-private37- do not use `final` on fields — exception: `static final` for constants like `LOGGER`38- do not use `final` on local variables or parameters39- do not use constructor injection — prefer field injection in CDI contexts40- avoid mutable static fields4142## Interfaces & Classes4344- only use interfaces with multiple implementations or for the strategy pattern; never create an interface whose only purpose is to be implemented by one class45- for stateless or procedural logic, prefer interfaces with `static` methods over classes with private constructors46- in utility interfaces, prefer `static` over `default` methods47- avoid anonymous inner classes — extract them into named, testable top-level classes (e.g. a CDI bean produced via `@Produces`) instead of instantiating an interface inline48- use records by default for value types and data carriers49- use sealed interfaces or sealed classes for closed type hierarchies (pairs well with pattern matching)50- prefer factory methods (static `of`, `from`, etc.) in records over passing `null` to constructors51- prefer composition over inheritance52- create multiple classes only if it decreases complexity and increases readability5354## Naming5556- name classes, modules, and files after their responsibilities, not technical concerns57- avoid meaningless suffixes: `*Impl`, `*Service`, `*Manager`, `*Creator`58- class names must not end with `Control`59- reserve protocol- or pattern-specific suffixes for elements that actually fulfill that role: `Resource` for JAX-RS classes, `Factory` for actual GoF factories, `Builder` for classes with method chaining60- avoid the `get` prefix; use the record-style convention — `configuration()` not `getConfiguration()`6162## Methods & Lambdas6364- keep methods short, cohesive, and testable65- create well-named methods for coarse-grained, self-contained logic66- never use multi-statement lambdas — extract them into well-named helper methods67- prefer method references over equivalent lambdas (`String::strip` over `s -> s.strip()`, `this::isSkillFile` over `p -> p.endsWith("SKILL.md")`)68- extract inline lambda predicates into explaining methods and use method references69- split complex `.filter()` calls with multiple `&&`/`||` conditions into chained `.filter()` calls70- extract complex boolean conditions into named predicate methods — write `boolean isEligible()` instead of inlining `age >= 18 && status.equals("active") && !banned`71- extract non-trivial calculations into named methods so call sites read as intent, not arithmetic72- do not create empty delegate methods that only forward without added value7374## Stream & Collections7576- prefer `java.util.stream.Stream` API over `for` loops77- avoid `forEach`; prefer terminal operations that return values78- prefer `Stream.of` over `Arrays.stream` for known elements79- prefer `.toList()` over `.collect(Collectors.toList())`80- prefer `List.of` / `Set.of` / `Map.of` over `new ArrayList<>()` and array literals for small immutable collections81- avoid creating unnecessary intermediate collections when streaming arrays82- prefer `Stream.gather(...)` with a `Gatherer` (Java 24+) over custom `Spliterator` or stateful `forEach` for stream transformations that need to keep state across elements or flush a remainder at end-of-stream83- prefer a named intermediate variable over deeply nested method chaining when readability suffers84- return empty collections (`List.of()`, `Set.of()`, `Map.of()`), never `null`85- do not put `null` values in collections8687## Code Style8889- KISS and YAGNI — always implement the simplest possible solution that works90- never over-engineer; ask before adding optional features, extension points, or abstractions91- code must be as simple, elegant, and understandable as possible92- always choose the simplest API — prefer higher-level, concise APIs over verbose low-level ones93- prefer multiple simpler lines to one complex line94- use text blocks (`"""`) for all multiline string content (JSON, SQL, HTML, help/usage text, templates) — never `+`-concatenation or embedded `\n` escapes95- prefer `String.formatted()` (instance) over `String.format(...)` (static) for readability at the call site96- prefer imports over fully qualified class names; remove unused imports97- prefer `Files.readString` / `Files.writeString` / `Files.lines` over `BufferedReader`/`BufferedWriter` ceremony98- no blank lines between imports99- use `this` to reference instance fields when it improves clarity100- prefer enums over plain strings for finite, well-defined values; reuse existing enum constants as values where possible (enum constants do not have to follow naming conventions when reused as values)101- prefer try-with-resources over manual `.close()` on any `AutoCloseable`102- extract repeated string literals into named constants — define once, change once103- prefer character literals and named constants over raw numeric literals — write `'\n'` not `10`, define `int ESC = '\033'` instead of inlining `27`104- inline single-use variables — if assigned and used only once on the next line, pass the expression directly105- bind behavior to data with functional fields — store a `Runnable`, `Consumer`, or lambda in a record instead of switching on type externally106- separate side effects from conditions — do the work first, then branch on the result; keep the `if` a pure decision107- use guard clauses (early returns) over deeply nested `if`/`else`108- avoid `Optional` as a parameter type; use `Optional` as a return type only when absence is a meaningful part of the contract109110## Exceptions111112- prefer unchecked over checked exceptions113- never throw raw `java.lang.Exception` or `RuntimeException` directly — throw a specific subclass114- do not re-throw with `throw e` adding no value115- do not catch and silently ignore exceptions — at minimum, log with context or rethrow wrapped116- use the underscore `_` for unused catch parameters (`catch (IOException _)`) instead of named variables like `e` or `ignored`117- create custom exceptions only when they significantly improve robustness or maintainability118119## Logging120121- use `java.lang.System.Logger` instead of `System.out` statements122- never use `java.util.logging.Logger`123- `Logger` fields must be named `LOGGER` (uppercase) and marked as `static final`124125## HTTP Client126127- prefer the synchronous `java.net.http.HttpClient` APIs128- use asynchronous APIs (`HttpClient.sendAsync`) only when explicitly requested129130## Testing131132- use AssertJ assertions instead of JUnit assertions133- unit test methods must not start with `test` or `should`134- create minimalistic tests first; avoid repetitive or trivial unit tests and keep only essential tests verifying core functionality135- do not write tests for implementations that cannot fail (enums, records, getters/setters)136- generate at most three tests per class under test (applies separately to unit, integration, and system tests)137- the presence of an `isEqualTo` assertion makes less specific checks (`startsWith`, `isNotNull`) obsolete138139## Comments & JavaDoc140141- default to no comments142- only comment when the *why* is non-obvious: hidden constraint, subtle invariant, workaround for a specific bug, behavior that would surprise a reader143- do not explain *what* the code does — well-named identifiers do that144- do not write JavaDoc that restates the method signature or rephrases the code145- either describe the *why* or omit the comment entirely146- when JavaDoc is written, always use Markdown JavaDoc (`///`, JEP 467); never use HTML-tagged `/** */` blocks147148## Composition with Other Skills149150- this skill defines only language-level Java conventions; build, packaging, file layout, frameworks, protocols, and architecture come from the composed skill (e.g. `java-cli-script`, `java-cli-app`, `microprofile-server`, `bce`, `zb`)151- when a composed skill adds or refines a rule (e.g. "use `IO.println` not `System.out.println`", "no package declaration in single-file scripts", "JAX-RS resources are boundary classes"), apply it on top of these rules; the composed skill always specializes, never contradicts152- if a composed skill is silent on a topic covered here, these rules apply by default