From java
Explain Java syntax availability across LTS baselines, compare expression forms between Java versions, rewrite code for older or newer targets, and choose foundational java.base package families. Use when the user asks about Java grammar, var, switch expressions, records, pattern matching, sealed classes, text blocks, unnamed patterns, or whether a syntax form compiles on a given Java baseline.
npx claudepluginhub ririnto/sinon --plugin javaThis skill uses the workspace's default tool permissions.
Explain Java syntax, LTS-boundary language differences, and foundational `java.base` coverage when it materially affects how code is written, read, or refactored. The common case is checking the target Java LTS baseline first, then choosing the clearest stable syntax and the smallest baseline-safe standard-library surface available on that baseline.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Explain Java syntax, LTS-boundary language differences, and foundational java.base coverage when it materially affects how code is written, read, or refactored. The common case is checking the target Java LTS baseline first, then choosing the clearest stable syntax and the smallest baseline-safe standard-library surface available on that baseline.
8, 11, 17, 21, or 25) before recommending any version-sensitive syntax.java.base guidance here as foundational standard-library coverage, not as a claim about broader Java SE modules or JDK tooling.java.base usage.java.base, anchor to foundational families such as collections, time, files, regex, or Optional before suggesting extra dependencies.17, 21, or 25.(JDK 8+) means safe on Java 8 and later LTS targets.(JDK 11+) means available on Java 11 and later.(JDK 17+), (JDK 21+), (JDK 25+) mean an LTS-boundary upgrade, not a universal fallback.Version-aware switch comparison (JDK 17+) vs fallback (JDK 8+):
String result = switch (status) {
case OK -> "ok";
case FAIL -> "fail";
default -> "unknown";
};
String result;
switch (status) {
case OK:
result = "ok";
break;
case FAIL:
result = "fail";
break;
default:
result = "unknown";
}
Use when: you need a quick answer to "can I use this on Java X?"
Text block for multiline literals (JDK 17+):
String sql = """
select *
from users
where active = true
order by created_at desc
""";
Record (JDK 17+):
record Point(int x, int y) {
}
(JDK 8+)Single-expression lambda:
import java.util.Comparator;
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());
Block-body lambda:
import java.util.function.Consumer;
Consumer<String> logger = message -> {
System.err.println(message);
};
No-argument lambda:
Runnable task = () -> runCleanup();
Type-inferred lambda with functional interface:
import java.util.function.Function;
Function<String, Integer> lengthOf = String::length;
(JDK 8+)Static method reference:
import java.util.function.Function;
Function<String, Integer> parser = Integer::parseInt;
Instance method reference on arbitrary object:
import java.util.function.Function;
Function<String, Integer> lengthOf = String::length;
Bound instance method reference:
import java.util.function.Consumer;
Consumer<String> printer = System.out::println;
Constructor reference:
import java.util.ArrayList;
import java.util.function.Supplier;
Supplier<ArrayList<String>> newList = ArrayList::new;
ArrayList<String> buffer = newList.get();
(JDK 8+, .toList() requires JDK 17+)Basic filter-map-collect using the unmodifiable toList() terminator (JDK 17+):
import java.util.List;
List<String> activeNames = users.stream()
.filter(User::isActive)
.map(User::name)
.toList();
Older-LTS fallback using Collectors.toList() (JDK 8+) — the official javadoc makes no guarantee about the returned List implementation, its mutability, its serializability, or its thread-safety; current HotSpot builds happen to return a mutable ArrayList, but code MUST NOT rely on that. When mutability matters, use Collectors.toCollection(ArrayList::new); when an unmodifiable result is part of the contract, use Collectors.toUnmodifiableList() (JDK 10+) or Stream.toList() on JDK 16+.
import java.util.List;
import java.util.stream.Collectors;
List<String> activeNames = users.stream()
.filter(User::isActive)
.map(User::name)
.collect(Collectors.toList());
Grouping and counting (JDK 8+):
import java.util.Map;
import java.util.stream.Collectors;
Map<String, Long> countByRole = users.stream()
.collect(Collectors.groupingBy(User::role, Collectors.counting()));
Flat map for nested collections (uses Stream.toList(), so target (JDK 17+)):
import java.util.List;
List<String> allTags = orders.stream()
.flatMap(order -> order.tags().stream())
.distinct()
.toList();
Optional pipeline (JDK 8+)import java.util.Optional;
Optional<Integer> timeout = Optional.ofNullable(config)
.map(Config::timeout)
.filter(t -> t > 0);
(JDK 9+, practical LTS: JDK 11+)import java.util.List;
import java.util.Map;
import java.util.Set;
List<String> roles = List.of("reader", "writer");
Set<String> perms = Set.of("read", "write");
Map<String, Integer> scores = Map.of("alice", 90, "bob", 85);
CompletableFuture async composition (JDK 8+)Basic async chain:
import java.util.concurrent.CompletableFuture;
CompletableFuture<String> result = CompletableFuture
.supplyAsync(() -> fetchUser(id))
.thenApply(User::name)
.thenCompose(name -> fetchAvatar(name))
.exceptionally(ex -> "default-avatar");
Combine multiple futures:
import java.util.concurrent.CompletableFuture;
CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> fetchUser(id));
CompletableFuture<String> permFuture = CompletableFuture.supplyAsync(() -> fetchPerms(id));
CompletableFuture<String> combined = userFuture.thenCombine(permFuture,
(user, perms) -> user + ":" + perms);
HttpClient API (JDK 11+)Synchronous request:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.header("Accept", "application/json")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
Asynchronous request:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
CompletableFuture<HttpResponse<String>> future = client.sendAsync(
request, HttpResponse.BodyHandlers.ofString());
(JDK 11+)boolean blank = " ".isBlank();
String stripped = " hello ".strip();
String repeated = "ha".repeat(3);
(JDK 8+)interface LogFormatter {
String format(String message);
default String formatWithPrefix(String prefix, String message) {
return prefix + format(message);
}
static LogFormatter prefixed(String prefix) {
return message -> prefix + message;
}
}
yield (JDK 17+)int code = switch (status) {
case OK -> 0;
case FAIL -> {
System.err.println("failure detected");
yield 1;
}
default -> -1;
};
(JDK 17+)sealed interface PaymentResult permits Approved, Rejected {
}
record Approved(String authorizationId) implements PaymentResult {
}
record Rejected(String reason) implements PaymentResult {
}
instanceof (JDK 17+)if (obj instanceof String s) {
use(s);
}
(JDK 21+)Exhaustive over a sealed hierarchy (no default needed because the permitted
subtypes are enumerated):
sealed interface Shape permits Circle, Rectangle {
}
record Circle(double radius) implements Shape {
}
record Rectangle(double width, double height) implements Shape {
}
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
};
Switch with guarded patterns (when clause) (JDK 21+):
String label = switch (value) {
case String s when s.length() > 10 -> s.substring(0, 7) + "...";
case String s -> s;
case Integer i -> "int:" + i;
default -> "unknown";
};
(JDK 21+)import java.util.ArrayList;
import java.util.List;
import java.util.SequencedCollection;
SequencedCollection<String> items = new ArrayList<>(List.of("a", "b", "c"));
String first = items.getFirst();
String last = items.getLast();
SequencedCollection<String> reversed = items.reversed();
items.addFirst("z");
items.addLast("d");
(JDK 25+)if (obj instanceof Order(String id, _, double total)) {
audit(id, total);
}
(JDK 11+)import java.util.List;
import java.util.function.Predicate;
var users = List.of("alice", "bob", "carol");
var count = users.size();
Predicate<String> lengthOver3 = (var name) -> name.length() > 3;
Notes:
var is for local variables; it does not change runtime types.var inside lambda parameter lists ((var name) -> ...) requires JDK 11 or later.(JDK 8+)String result;
switch (status) {
case OK:
result = "ok";
break;
case FAIL:
result = "fail";
break;
default:
result = "unknown";
}
java.base drifts toward jdk.* tools, jdeps, jlink, jpackage, runtime images, packaging chains, or live JVM diagnostics, stop and clarify that those are outside this skill's scope.Return:
| If the blocker is... | Open... |
|---|---|
| exact LTS-boundary availability, migration heuristics, or later-LTS recipes | advanced-syntax-recipes.md |
choosing a foundational java.base package family | java-base-family-map.md |
java.base as if it covered all jdk.* tools or every Java SE module.