Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
# CLAUDE.md — Modern Java
## Target version
- Java 21+ (LTS). Java 25 if the runtime supports it.
- `--release 21` in the compiler. Don't ship code that compiles on a newer JDK but doesn't run on the target.
- Pin the JDK in `.tool-versions` / `.sdkmanrc` / `.java-version` so every machine builds against the same toolchain.
## Records, sealed types, pattern matching
- Use `record` for value objects. They give you constructor, accessors, `equals`, `hashCode`, `toString` for free.
- `sealed interface` + permitted `record` subtypes for discriminated-union-style modeling:
```java
sealed interface Result<T> permits Result.Ok, Result.Err {
record Ok<T>(T value) implements Result<T> {}
record Err<T>(String message) implements Result<T> {}
}
```
- `switch` expressions with pattern matching for exhaustive dispatch:
```java
return switch (result) {
case Result.Ok<T>(var v) -> v;
case Result.Err<T>(var m) -> throw new IllegalStateException(m);
};
```
- Compiler-checked exhaustiveness on sealed types. Don't use `default` if you can list the cases.
## Language defaults
- `var` for local variables when the right-hand side makes the type obvious. Don't use `var` for ambiguous returns (`var x = service.get();`).
- Text blocks (`"""`) for multi-line strings — never `+ "\n"` chains.
- `String.format` for one-off formatting; `String.join` for concatenation; `StringBuilder` only in tight loops.
- `Optional<T>` as a return type, never as a field or parameter.
## Null handling
- Treat `null` as a code smell. Prefer `Optional`, sentinel values, or sealed types.
- Annotate parameters and returns with `@NonNull` / `@Nullable` (JSpecify or JetBrains).
- `Objects.requireNonNull(arg, "arg")` at the top of public methods that don't tolerate null.
## Concurrency
- Virtual threads (`Thread.startVirtualThread`, `Executors.newVirtualThreadPerTaskExecutor()`) for I/O-bound work. They scale to millions.
- Structured concurrency (`StructuredTaskScope`) for fan-out/fan-in. Cleanup is automatic on scope exit.
- `ConcurrentHashMap` over `Collections.synchronizedMap`. Read the JavaDoc on the iteration semantics.
- Don't share mutable state across threads without a memory-barrier guarantee.
## Build & tooling
- **Maven** or **Gradle**. Pick one; commit to it. Wrapper script (`./mvnw`, `./gradlew`) is committed.
- Set `--enable-preview` only if you actually use preview features. Preview features may change between releases.
- Format with **google-java-format** or **palantir-java-format**. Lint with **error-prone**, **PMD**, or **SpotBugs**.
## Don't
- Don't use `Optional.get()` without `isPresent()`. Use `orElse`, `orElseThrow`, or `ifPresent`.
- Don't catch `Exception` to "be safe". Catch the narrowest type or let it propagate.
- Don't write `for (int i = 0; i < list.size(); i++)`. Use enhanced-for, streams, or `IntStream.range`.
- Don't use `synchronized` on `String` literals or boxed primitives — pool aliasing creates shared locks.
Other Java templates
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.
Spring Framework Core Rules
Dependency injection, bean lifecycle, configuration classes, and profiles.