java
Use when writing Java (LTS releases such as 21) with Gradle — command-line tools with picocli, records and sealed types for domain models, settlement or allocation algorithms, JUnit 5 tests and runnab
SKILL.mdskills/languages/java/SKILL.md
GitHubJava
Default: the current LTS JDK, Gradle with the wrapper (./gradlew) and Kotlin DSL, picocli for CLIs (Spring Boot when you need a long-running service), JUnit 5 + AssertJ, application plugin for a runnable distribution.
Setup
gradle init --type java-application --dsl kotlin, commit the wrapper (gradlew,gradle/wrapper/*).- Set a toolchain in
build.gradle.ktsso Gradle downloads or selects the right JDK regardless of the machine's default:java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } - Enable
-Xlint:all -WerrorforcompileJava.
Modelling with modern Java
recordfor immutable data (record Payment(String payer, long amountMinor, Set<String> sharedBy)).sealed interface+switchpattern matching for closed sets of cases; the compiler checks exhaustiveness.longminor units orBigDecimalwith an explicitRoundingModefor money; neverdouble.
Example: settling shared expenses
- Compute each person's balance = paid − owed.
- Distribute rounding remainders deterministically (e.g. to the people with the largest fractional parts, ties by name) and test that balances sum to exactly zero.
- Greedy matching of the largest debtor with the largest creditor gives at most n−1 transfers; that is good enough in practice (the true minimum is NP-hard in general).
- Print transfers in a stable order so output is diffable.
CLI with picocli
- Annotate a
Callable<Integer>with@Command(mixinStandardHelpOptions = true); return the exit code. - Keep parsing in the command class and logic in plain classes.
- Package with
./gradlew installDist(scripts + jars) or a fat jar if a single file is required.
Testing
- JUnit 5
@ParameterizedTestfor tables of inputs; property checks (e.g. with jqwik) for invariants like "balances sum to zero". ./gradlew testshould not need network access after the first dependency download.
Verification loop
./gradlew build(compiles with lint as errors, runs tests)../gradlew run --args="--help"and a real example.- Run the distribution from
build/install/on a machine with only a JRE.
Pitfalls
equalsonBigDecimalcompares scale (2.0≠2.00); usecompareTofor numeric equality.Integercaching:==on boxed integers works only for small values; always useequalsor unbox.- Default charset and locale: pass
StandardCharsets.UTF_8andLocale.ROOTfor files and formatting. - Gradle daemons and caches can hide configuration problems; reproduce CI failures with
--no-daemon --no-build-cache. - Records with mutable components (lists) are only shallowly immutable; copy with
List.copyOfin the compact constructor.