All Java templates

Modern Java Rules

Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.

DevZone Tools3,360 copiesUpdated Apr 21, 2026Java
# 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