From java
Design idiomatic Java APIs, review class structure for immutability and clarity, choose between records and sealed classes, decide checked vs unchecked exception boundaries, and shape public contracts with narrow surfaces and explicit value semantics. Use when the user asks to design a Java API, review Java class structure, refactor Java code, or needs guidance on idiomatic Java language and library design.
npx claudepluginhub ririnto/sinon --plugin javaThis skill uses the workspace's default tool permissions.
Produce idiomatic, maintainable Java code and API designs. The common case is choosing a clearer type shape, a narrower contract, and an unsurprising exception and mutability model before touching framework details.
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.
Produce idiomatic, maintainable Java code and API designs. The common case is choosing a clearer type shape, a narrower contract, and an unsurprising exception and mutability model before touching framework details.
public, protected, package-private, then private.Start from one explicit value carrier and one explicit capability interface:
public record CustomerId(String value) {
}
public interface PaymentGateway {
Receipt charge(ChargeRequest request);
}
Use when: tightening a contract or replacing a vague mutable DTO or service surface.
public final class RetryPolicy {
private final int maxAttempts;
private RetryPolicy(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public static RetryPolicy of(int maxAttempts) {
if (maxAttempts < 1) {
throw new IllegalArgumentException("maxAttempts must be positive");
}
return new RetryPolicy(maxAttempts);
}
}
When a type has many optional parameters and a factory method becomes unwieldy:
public final class QueryOptions {
private final int limit;
private final int offset;
private final String sortBy;
private final boolean ascending;
private QueryOptions(Builder builder) {
this.limit = builder.limit;
this.offset = builder.offset;
this.sortBy = builder.sortBy;
this.ascending = builder.ascending;
}
public int limit() {
return limit;
}
public int offset() {
return offset;
}
public String sortBy() {
return sortBy;
}
public boolean ascending() {
return ascending;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private int limit = 100;
private int offset = 0;
private String sortBy = "id";
private boolean ascending = true;
public Builder limit(int limit) {
if (limit < 1) {
throw new IllegalArgumentException("limit must be positive");
}
this.limit = limit;
return this;
}
public Builder offset(int offset) {
if (offset < 0) {
throw new IllegalArgumentException("offset must be >= 0");
}
this.offset = offset;
return this;
}
public Builder sortBy(String sortBy) {
this.sortBy = sortBy;
return this;
}
public Builder ascending(boolean ascending) {
this.ascending = ascending;
return this;
}
public QueryOptions build() {
return new QueryOptions(this);
}
}
}
import java.util.Objects;
public final class Money {
private final String currency;
private final long cents;
public Money(String currency, long cents) {
this.currency = Objects.requireNonNull(currency);
this.cents = cents;
}
@Override
public boolean equals(Object o) {
return o instanceof Money m
&& currency.equals(m.currency)
&& cents == m.cents;
}
@Override
public int hashCode() {
return Objects.hash(currency, cents);
}
}
public final class Example {
private static final String TYPE = "example";
private final String value;
public Example(String value) {
this.value = value;
}
public static Example of(String value) {
return new Example(value);
}
@Override
public String toString() {
return value;
}
public String value() {
return value;
}
static final class Parser {
}
}
import java.util.List;
public List<String> roles() {
return List.copyOf(roles);
}
import java.io.IOException;
public Receipt load(String id) throws IOException {
return gateway.load(id);
}
Use ? extends T for input (producer) and ? super T for output (consumer):
import java.util.Collection;
import java.util.List;
public void processAll(Collection<? extends Task> tasks) {
tasks.forEach(Task::run);
}
public void addAll(List<? super String> target, List<String> source) {
target.addAll(source);
}
@FunctionalInterface
public interface RetryStrategy {
boolean shouldRetry(int attempt, Throwable lastFailure);
}
final classes with manual equality and constructor validation.Return:
| If the blocker is... | Open... |
|---|---|
| records, sealed classes, and semantic modeling tradeoffs once the Java baseline is known | language-features.md |
| mutability, collection exposure, visibility, or exception-contract review | api-design.md |