
Java Kotlin
- 42 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
java-kotlin is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- java-kotlin
- AI & Agent Building
- AI-coding skill
Java Kotlin by the numbers
- 42 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #8,070 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill java-kotlinAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 42 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Java & Kotlin
Overview
Modern Java and Kotlin patterns for JVM development.
---
Java Modern Features
Records and Sealed Classes (Java 17+)
// Record - immutable data class
public record User(
String id,
String email,
String name,
Instant createdAt
) {
// Compact constructor for validation
public User {
Objects.requireNonNull(id);
Objects.requireNonNull(email);
if (!email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}
// Static factory method
public static User create(String email, String name) {
return new User(UUID.randomUUID().toString(), email, name, Instant.now());
}
}
// Sealed classes - restricted hierarchy
public sealed interface Shape
permits Circle, Rectangle, Triangle {
double area();
}
public record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public record Rectangle(double width, double height) implements Shape {
@Override
public double area() {
return width * height;
}
}
public final class Triangle implements Shape {
private final double base;
private final double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
}Pattern Matching
// Pattern matching for instanceof (Java 16+)
public String describe(Object obj) {
if (obj instanceof String s) {
return "String of length " + s.length();
} else if (obj instanceof Integer i) {
return "Integer: " + i;
} else if (obj instanceof List<?> list && !list.isEmpty()) {
return "Non-empty list with " + list.size() + " elements";
}
return "Unknown type";
}
// Pattern matching for switch (Java 21+)
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
// Guard patterns
public String classify(Shape shape) {
return switch (shape) {
case Circle c when c.radius() > 10 -> "Large circle";
case Circle c -> "Small circle";
case Rectangle r when r.width() == r.height() -> "Square";
case Rectangle r -> "Rectangle";
default -> "Other shape";
};
}Streams and Optionals
import java.util.stream.*;
import java.util.Optional;
public class StreamExamples {
// Basic operations
public List<String> processUsers(List<User> users) {
return users.stream()
.filter(u -> u.active())
.map(User::name)
.sorted()
.distinct()
.collect(Collectors.toList());
}
// Grouping
public Map<String, List<User>> groupByDomain(List<User> users) {
return users.stream()
.collect(Collectors.groupingBy(
u -> u.email().substring(u.email().indexOf("@") + 1)
));
}
// Statistics
public DoubleSummaryStatistics getStats(List<Order> orders) {
return orders.stream()
.mapToDouble(Order::total)
.summaryStatistics();
}
// Parallel processing
public long countLargeFiles(Path directory) throws IOException {
try (Stream<Path> paths = Files.walk(directory)) {
return paths
.parallel()
.filter(Files::isRegularFile)
.filter(p -> {
try {
return Files.size(p) > 1_000_000;
} catch (IOException e) {
return false;
}
})
.count();
}
}
// Optional handling
public String getUserEmail(Long userId) {
return findUser(userId)
.map(User::email)
.filter(email -> !email.isBlank())
.orElse("unknown@example.com");
}
public User getOrCreate(Long userId) {
return findUser(userId)
.orElseGet(() -> createUser(userId));
}
}---
Kotlin Fundamentals
Data Classes and Null Safety
// Data class (like Java record)
data class User(
val id: String = UUID.randomUUID().toString(),
val email: String,
val name: String,
val createdAt: Instant = Instant.now()
) {
init {
require(email.contains("@")) { "Invalid email" }
}
}
// Null safety
fun processUser(user: User?) {
// Safe call
val name = user?.name
// Elvis operator
val displayName = user?.name ?: "Anonymous"
// Smart cast after null check
if (user != null) {
println(user.email) // user is User, not User?
}
// let for null-safe operations
user?.let {
sendEmail(it.email)
}
// Not-null assertion (use sparingly)
val email = user!!.email // throws if null
}
// Platform types from Java
fun handleJavaString(javaString: String?) {
// Treat Java strings as nullable
val length = javaString?.length ?: 0
}Extension Functions and Properties
// Extension function
fun String.toSlug(): String =
this.lowercase()
.replace(Regex("[^a-z0-9]+"), "-")
.trim('-')
// Extension property
val String.wordCount: Int
get() = this.split(Regex("\\s+")).size
// Extension on nullable type
fun String?.orEmpty(): String = this ?: ""
// Usage
val slug = "Hello World".toSlug() // "hello-world"
val count = "Hello World".wordCount // 2
// Scope functions
data class Config(var host: String = "", var port: Int = 0)
val config = Config().apply {
host = "localhost"
port = 8080
}
val result = user.let { it.name.uppercase() }
val processedUser = user.also { log.info("Processing ${it.id}") }
val transformed = with(user) {
"$name <$email>"
}Coroutines
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// Suspend function
suspend fun fetchUser(id: String): User {
return withContext(Dispatchers.IO) {
api.getUser(id)
}
}
// Concurrent execution
suspend fun fetchAllUsers(ids: List<String>): List<User> = coroutineScope {
ids.map { id ->
async { fetchUser(id) }
}.awaitAll()
}
// Structured concurrency
suspend fun processOrders(orders: List<Order>) = coroutineScope {
orders.forEach { order ->
launch {
processOrder(order)
}
}
}
// Exception handling
suspend fun safeFetch(id: String): Result<User> = runCatching {
fetchUser(id)
}
// Flow (cold stream)
fun fetchUsers(): Flow<User> = flow {
val users = api.getAllUsers()
users.forEach { user ->
emit(user)
delay(100)
}
}
// Flow operators
suspend fun processUserFlow() {
fetchUsers()
.filter { it.active }
.map { it.name }
.catch { e -> emit("Error: ${e.message}") }
.collect { name ->
println(name)
}
}
// StateFlow for state management
class UserViewModel : ViewModel() {
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
fun loadUser(id: String) {
viewModelScope.launch {
_state.value = UiState.Loading
try {
val user = fetchUser(id)
_state.value = UiState.Success(user)
} catch (e: Exception) {
_state.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
}Sealed Classes and When
// Sealed class hierarchy
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String, val cause: Throwable? = null) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// Exhaustive when
fun <T> handleResult(result: Result<T>): String = when (result) {
is Result.Success -> "Success: ${result.data}"
is Result.Error -> "Error: ${result.message}"
Result.Loading -> "Loading..."
}
// Sealed interface
sealed interface UiEvent {
data class ShowMessage(val message: String) : UiEvent
data class Navigate(val route: String) : UiEvent
object GoBack : UiEvent
}
// When with guards
fun describe(value: Any): String = when {
value is String && value.isEmpty() -> "Empty string"
value is String -> "String: $value"
value is Int && value > 0 -> "Positive int"
value is Int -> "Non-positive int"
else -> "Unknown"
}---
Functional Patterns
// Higher-order functions
inline fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) {
result.add(item)
}
}
return result
}
// Function composition
infix fun <A, B, C> ((B) -> C).compose(other: (A) -> B): (A) -> C = { a ->
this(other(a))
}
val double: (Int) -> Int = { it * 2 }
val addOne: (Int) -> Int = { it + 1 }
val doubleThenAddOne = addOne compose double
println(doubleThenAddOne(3)) // 7
// Currying
fun add(a: Int): (Int) -> Int = { b -> a + b }
val add5 = add(5)
println(add5(3)) // 8
// Memoization
fun <T, R> ((T) -> R).memoize(): (T) -> R {
val cache = mutableMapOf<T, R>()
return { key ->
cache.getOrPut(key) { this(key) }
}
}
val fibonacci: (Int) -> Long = { n: Int ->
if (n <= 1) n.toLong()
else fibonacci(n - 1) + fibonacci(n - 2)
}.memoize()---
DSL Building
// Type-safe builder DSL
class HtmlBuilder {
private val children = mutableListOf<String>()
fun head(block: HeadBuilder.() -> Unit) {
children.add(HeadBuilder().apply(block).build())
}
fun body(block: BodyBuilder.() -> Unit) {
children.add(BodyBuilder().apply(block).build())
}
fun build() = "<html>${children.joinToString("")}</html>"
}
class BodyBuilder {
private val children = mutableListOf<String>()
fun div(classes: String = "", block: BodyBuilder.() -> Unit = {}) {
children.add("<div class=\"$classes\">${BodyBuilder().apply(block).build()}</div>")
}
fun p(text: String) {
children.add("<p>$text</p>")
}
fun build() = children.joinToString("")
}
fun html(block: HtmlBuilder.() -> Unit) = HtmlBuilder().apply(block).build()
// Usage
val page = html {
body {
div("container") {
p("Hello, World!")
}
}
}
// Test DSL
class TestContext {
fun given(description: String, block: () -> Unit) {
println("Given: $description")
block()
}
fun whenever(description: String, block: () -> Unit) {
println("When: $description")
block()
}
fun then(description: String, block: () -> Unit) {
println("Then: $description")
block()
}
}
fun test(name: String, block: TestContext.() -> Unit) {
println("Test: $name")
TestContext().apply(block)
}
// Usage
test("user registration") {
given("a new user email") { }
whenever("registering the user") { }
then("user should be created") { }
}---
Related Skills
- [[backend]] - Spring Boot development
- [[mobile]] - Android development
- [[testing]] - JUnit, Kotest
/**
* Kotlin/JVM Project Template
* Usage: Copy to project root
*/
plugins {
kotlin("jvm") version "1.9.22"
kotlin("plugin.serialization") version "1.9.22"
application
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.gitlab.arturbosch.detekt") version "1.23.4"
}
group = "com.example"
version = "1.0.0"
repositories {
mavenCentral()
}
dependencies {
// Kotlin
implementation(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
// HTTP Client
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-cio:2.3.7")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
// Logging
implementation("io.github.microutils:kotlin-logging-jvm:3.0.5")
implementation("ch.qos.logback:logback-classic:1.4.14")
// Testing
testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.13.8")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
testImplementation("io.kotest:kotest-assertions-core:5.8.0")
}
kotlin {
jvmToolchain(17)
}
application {
mainClass.set("com.example.MainKt")
}
tasks {
test {
useJUnitPlatform()
}
shadowJar {
archiveBaseName.set("app")
archiveClassifier.set("")
archiveVersion.set("")
manifest {
attributes["Main-Class"] = "com.example.MainKt"
}
}
}
detekt {
config.setFrom("$projectDir/detekt.yml")
buildUponDefaultConfig = true
}
// ===========================================
// Task Aliases
// ===========================================
tasks.register("lint") {
dependsOn("detekt")
}
tasks.register("package") {
dependsOn("shadowJar")
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
Maven Project Template
Usage: Copy to project root
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>My Application</name>
<description>Project description</description>
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Dependency versions -->
<spring-boot.version>3.2.1</spring-boot.version>
<lombok.version>1.18.30</lombok.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<junit.version>5.10.1</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Utilities -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Compiler with annotation processors -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- Surefire for tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.3</version>
</plugin>
<!-- JaCoCo for coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!-- Development profile -->
<profile>
<id>dev</id>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
</profile>
<!-- Production profile -->
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>
</project>
Java/Kotlin Templates
Build configuration templates for JVM projects.
Files
| Template | Purpose |
|---|---|
build.gradle.kts | Kotlin DSL Gradle build (Kotlin/JVM) |
pom.xml | Maven build (Spring Boot) |
Usage
Gradle (Kotlin)
# Copy template
cp templates/build.gradle.kts ./build.gradle.kts
# Initialize wrapper
gradle wrapper --gradle-version 8.5
# Common commands
./gradlew build # Build project
./gradlew test # Run tests
./gradlew shadowJar # Create fat JAR
./gradlew detekt # Run linterMaven (Java)
# Copy template
cp templates/pom.xml ./pom.xml
# Common commands
mvn clean install # Build project
mvn test # Run tests
mvn package # Create JAR
mvn spring-boot:run # Run Spring Boot appGradle Features
| Feature | Plugin/Dependency |
|---|---|
| Fat JAR | Shadow plugin |
| Coroutines | kotlinx-coroutines |
| HTTP Client | Ktor |
| Serialization | kotlinx-serialization |
| Testing | Kotest + MockK |
| Linting | Detekt |
Maven Features
| Feature | Dependency |
|---|---|
| Web Framework | Spring Boot |
| ORM | Spring Data JPA |
| Boilerplate | Lombok |
| Mapping | MapStruct |
| Testing | JUnit 5 + Testcontainers |
| Coverage | JaCoCo |
Project Structure
my-project/
├── build.gradle.kts # or pom.xml
├── settings.gradle.kts # Gradle settings
├── src/
│ ├── main/
│ │ ├── kotlin/ # Kotlin sources
│ │ ├── java/ # Java sources
│ │ └── resources/
│ │ └── application.yml
│ └── test/
│ ├── kotlin/
│ └── java/
└── gradle/
└── wrapper/Customization
Gradle - Change main class
application {
mainClass.set("com.yourcompany.MainKt")
}Maven - Change Java version
<properties>
<java.version>21</java.version>
</properties>Add new dependency (Gradle)
dependencies {
implementation("group:artifact:version")
}Add new dependency (Maven)
<dependency>
<groupId>group</groupId>
<artifactId>artifact</artifactId>
<version>version</version>
</dependency>Related skills
AI & Agent Buildingagents