← All Series
Complete 17/17 parts published

Kotlin for Java Survivors: Life After Semicolons

A sixteen-part recovery program for Java developers moving to Kotlin: fewer NullPointerExceptions, zero semicolons, coroutines instead of thread pools, and two Spring Boot 4.1 services to prove it, one with an AI assistant that knows what it doesn't know. Just enough syntactic sugar to question why you suffered so long.

0
Conceptual Complete

Kotlin for Java Survivors: Life After Semicolons

A 16-part series for senior Java and Spring developers learning Kotlin 2.4. Follow Kabir, a 14-year Java veteran, from culture shock to shipping two Spring Boot 4.1 services with Kafka, Qdrant, Spring AI and LangChain4j.

1
Conceptual Complete

"Life After Semicolons" — Syntax, Control Flow and the Build

Kabir's first Kotlin pull request is Java with the semicolons left in. We set up a Kotlin 2.4 Gradle build, see what top-level functions compile to, and cover val/var, types without primitives, strings, default arguments, if/when/try as expressions, ranges and loops.

2
Conceptual Complete

"The Billion-Dollar Fix" — Null Safety and the Type System

A NullPointerException from a Java supplier SDK crashes Kabir's Kotlin code. We cover nullable types and the safe-call operators, what they compile to, smart casts and their limits, platform types and JSpecify, lateinit vs lazy, Any/Unit/Nothing, and == vs ===.

3
Conceptual Complete

"Classes Without Ceremony" — Constructors, Properties and Objects

Kabir starts typing getters and Lena stops him. We cover properties instead of fields, accessors and explicit backing fields, primary constructors and init order, final-by-default classes, Kotlin's different protected and internal, objects and companions instead of static, nested vs inner classes, and enums with entries.

4
Conceptual Complete

"Data, Sealed and Value" — Modelling the Domain

Two swapped String arguments make a Pune till ring up toor dal at zero. We rebuild the catalog model with data classes and copy(), destructuring, sealed hierarchies with exhaustive when, value classes that cost nothing until they box, and type aliases, and see what each compiles to.

5
Conceptual Complete

"Functions Are Values" — Lambdas, References and Inline

A return inside forEach silently skips half of a pricing audit. We look at what a Kotlin lambda really is: function types instead of functional interfaces, closures over mutable variables, references, fun interfaces, inline functions and where each kind of return goes, plus tailrec and a first look at lambdas with receivers.

6
Conceptual Complete

"Extend, Scope, Delegate" — Kotlin's Power Tools

A region code and an audit log threaded through six layers of parameters start a design debate. We cover extension functions and their static dispatch, scope functions with a decision table, context parameters (Stable in 2.4), class and property delegation, operator overloading and infix calls, and build a type-safe product-query DSL with @DslMarker.

7
Conceptual Complete

"Collections Without Collectors" — Collections, Sequences and Strings

A Java utility sorts a "read-only" Kotlin list in place, and a nightly job drowns the garbage collector in intermediate lists. We look at what Kotlin collections are on the JVM, the operations that replace Collectors, sequences versus eager chains versus Streams, arrays, strings and regex, and kotlin.time and Uuid.

8
Conceptual Complete

"Generics Without Wildcards" — Variance, Reified and Constraints

A Java repository signature full of ? extends and ? super meets Kotlin, and most of the wildcards disappear. We cover declaration-site variance with out and in, use-site and star projections, bounds and where clauses, nullable type parameters and T & Any, erasure and @JvmName, reified type parameters, and build a typed event envelope, router and repository.

9
Conceptual Complete

"Errors Without Checked Exceptions" — Failure Modelling, Annotations and Reflection

The Java pricing team can't catch an IOException that Kotlin throws. We cover Kotlin's answer to checked exceptions: @Throws for Java callers, try as an expression, require/check/error, Result and runCatching (and their traps), sealed error types, the Experimental unused-return-value checker, annotation use-site targets under the Kotlin 2.4 defaults, and reflection basics.

10
Conceptual Complete

"Suspend Your Disbelief" — Coroutines Fundamentals

Cancelling an import leaves forty supplier calls running on virtual threads. We learn coroutines from the Java side: suspend functions and the state machine the compiler builds, launch/async/runBlocking, structured concurrency, coroutineScope vs supervisorScope, dispatchers and limitedParallelism, cooperative cancellation and timeouts, exception propagation, and how to debug it all.

11
Conceptual Complete

"Flowing Data" — Flow, Channels and Virtual Threads

Store managers want live stock numbers, and Kabir reaches for Flux. We cover cold Flow and its operators, back-pressure with buffer, conflate and debounce, hot StateFlow and SharedFlow, channels, Reactor interop in both directions, testing with runTest and virtual time, and a decision table for virtual threads versus coroutines.

12
Conceptual Complete

"Bilingual" — Java Interop, Build Plugins, Testing and Tooling

The Java team wraps Kabir's Kotlin library in a 140-line adapter, and he learns what his API looks like from the other side. We cover calling Java from Kotlin and Kotlin from Java (@JvmStatic, @JvmOverloads, @JvmField, @JvmName, @JvmRecord, @JvmExposeBoxed), the compiler plugins Spring and JPA need, compilerOptions, testing with JUnit, MockK, Kotest and power-assert, and code quality with ktlint, detekt and explicit API mode.

13
Project Complete

"The Catalog" — Spring Boot 4.1, JPA and REST in Kotlin

Kabir's first entity is a data class, and his first integration test dies with a StackOverflowError. We build product-catalog-service on Spring Boot 4.1 and Kotlin 2.4.20: entities that are not data classes, Spring Data with nullable returns, validation and Jackson 3 at the REST boundary, ProblemDetail errors, transactions that roll back on checked exceptions, the Gradle build, Testcontainers 2 and virtual threads.

14
Project Complete

"Reactive and Events" — Coroutines, Streaming and Kafka

The AI team needs every product change, and Lena asks who owns the event schema. We publish a sealed, kotlinx.serialization event hierarchy to Kafka after commit and stream it as server-sent events from a Flow. We fan out to suppliers with suspend controllers and a suspending @HttpExchange client, and keep the request id in the logs as coroutines hop threads, all on Spring MVC with virtual threads.

15
Project Complete

"The Assistant" — Spring AI 2.0, Qdrant and RAG

The prototype invented a return policy, and Kabir builds the service that can't. We build product-ai-service on Spring AI 2.0: a Kafka consumer that keeps a Qdrant index in step with the catalog, filtered semantic search, answers grounded in store policies, a tool for live prices, and a local model through Ollama, with the traps we hit on the way.

16
Project Complete

"The Second Opinion" — LangChain4j, Side by Side

The architecture review board asks what happens if Spring AI is the wrong bet, and Kabir answers with a second adapter. We implement the same ports with LangChain4j behind a profile, rebuild its index by replaying Kafka, run one test suite against both libraries, look at where Kotlin meets two Java APIs, and compare the libraries honestly. Then we wrap up the series.