Use when you have a written implementation plan to execute in a separate session with review checkpoints
npx skills add iceflower/opencode-agents-and-skills --skill "kotlin-migration"
Install specific skill from multi-skill repository
# Description
>-
# SKILL.md
name: kotlin-migration
description: >-
Kotlin version migration guide and breaking changes reference.
Use when upgrading Kotlin versions, replacing deprecated APIs,
or reviewing version-specific language features (K2, value class, Enum.entries, etc.).
Kotlin Version Migration Rules
1. Version Overview
| Version | Release | Key Theme |
|---|---|---|
| 1.3 | 2018-10 | Coroutines stable |
| 1.4 | 2020-08 | SAM conversions, trailing commas |
| 1.5 | 2021-05 | Value classes, sealed interfaces, JVM records |
| 1.6 | 2021-11 | Exhaustive when, builder inference stable |
| 1.7 | 2022-06 | K2 alpha, min/max API changes |
| 1.8 | 2022-12 | Java 8 minimum target, kotlin-reflect slim |
| 1.9 | 2023-07 | K2 beta, Enum.entries, data object |
| 2.0 | 2024-05 | K2 compiler stable, smart cast improvements |
| 2.1 | 2024-11 | Guard conditions in when, multi-dollar strings |
| 2.2 | 2025-06 | Context parameters stable, guard/break/continue stable |
| 2.3 | 2025-12 | Explicit backing fields, unused return value checker |
2. Kotlin 1.4 β 1.5
Value Classes (Replaces Inline Classes)
// 1.4: inline class (deprecated)
inline class UserId(val value: Long)
// 1.5+: value class
@JvmInline
value class UserId(val value: Long)
inline classis deprecated β replace with@JvmInline value class@JvmInlineis required on JVM backend
Sealed Interfaces
// 1.5+: sealed interface support
sealed interface Result<out T>
data class Success<T>(val data: T) : Result<T>
data class Failure(val error: Throwable) : Result<Nothing>
- Unlike
sealed class, sealed interfaces allow multiple inheritance - Prefer sealed interface when designing type hierarchies
Stable Unsigned Integer Types
val size: UInt = 42u
val bigId: ULong = 123456789uL
Migration Notes
- Bulk replacing
inline classβvalue classmay break binary compatibility - Adding
@JvmInlineto library modules changes ABI β coordinate with consumers
3. Kotlin 1.5 β 1.6
Exhaustive When Statements
sealed class Status { ... }
// 1.6+: warning when not all cases are covered in when-statement
// 1.7+: warning promoted to error
val label = when (status) {
is Status.Active -> "active"
is Status.Inactive -> "inactive"
// missing branch causes compile error
}
- Previously required only for when-expressions; now also applies to when-statements
- Adding new sealed subtypes requires updating all
whenblocks
Stable Builder Inference
// Type inference works reliably inside builder lambdas
buildList {
add("hello")
add("world")
} // Inferred as List<String>
Suspend Conversions
// Regular functions can be passed where suspend function type is expected
fun normalFunction(): String = "result"
suspend fun process(block: suspend () -> String) = block()
// 1.6+: direct passing allowed
process(::normalFunction)
Migration Notes
- Existing
whenstatements withoutelsebranch will produce warnings β addelseor cover all cases - Compiler option
-Xopt-inrenamed to-opt-in
4. Kotlin 1.6 β 1.7
min/max Function Behavior Change
// 1.6: NoSuchElementException on empty collection
listOf<Int>().min() // throws
// 1.7+: prefer minOrNull(), maxOrNull()
// min(), max() now return non-null (throw on empty collection)
listOf<Int>().minOrNull() // returns null
Definitely Non-Nullable Types
// Improved interop with Java generics
fun <T : Any> process(value: T & Any) { ... }
Opt-In Requirement Changes
// @RequiresOptIn annotation is now stable
@RequiresOptIn(message = "This API is experimental")
@Retention(AnnotationRetention.BINARY)
annotation class ExperimentalApi
Migration Notes
- Review
min()/max()usage β switch tominOrNull()/maxOrNull()if empty collection is possible - Some
@ExperimentalStdlibApiAPIs promoted to stable
5. Kotlin 1.7 β 1.8
JVM Target Minimum Version 1.8
// build.gradle.kts
kotlin {
jvmToolchain(17) // or 21
}
// jvmTarget = "1.6" removed in 1.8
jvmTarget = "1.6"and"1.7"are no longer supported- Minimum target is
"1.8"(Java 8)
kotlin-reflect Size Reduction
kotlin-reflectreduced from ~700KB to ~400KBjavax.xmldependency removed fromkotlin-reflect
Stable Recursive Copy for Directories
// Directory recursive copy is now stable
sourcePath.copyToRecursively(targetPath, followLinks = false)
Migration Notes
- Build fails if
jvmTargetis"1.6"or"1.7"β change to"1.8"or higher - Prefer
kotlin.jvmToolchain()overkotlinOptions.jvmTargetin Gradle
6. Kotlin 1.8 β 1.9
Enum.entries (Replaces values())
enum class Color { RED, GREEN, BLUE }
// Before 1.9: values() β allocates a new array on every call
Color.values().forEach { ... }
// 1.9+: entries β pre-allocated immutable list
Color.entries.forEach { ... }
values()copies the array on every call β performance overheadentriesreturnsList<E>with no reallocation
Data Objects
// 1.9+: data object β auto-generated toString()
sealed interface Result {
data class Success(val value: String) : Result
data object Loading : Result // toString() = "Loading"
data object Error : Result // toString() = "Error"
}
objecttoString() returnspackage.ClassName@hashCodedata objecttoString() returns just the class name β better for logging and debugging
..< (Open-Ended Range) Operator
// 1.9+: ..< operator replaces until
for (i in 0..<list.size) { ... }
// Equivalent: for (i in 0 until list.size)
@Volatile for Kotlin/Native
@Volatileannotation now supported on Kotlin/Native
Migration Notes
- Bulk replace
values()βentriesrecommended (performance improvement) - Singleton sealed subtypes: replace
objectβdata object untilβ..<replacement is optional (depends on readability preference)
7. Kotlin 1.9 β 2.0 (Major Version)
K2 Compiler (Stable)
- K2 compiler becomes the default compiler
- Up to 2x faster compilation speed
- More accurate type inference and smart casts
Smart Cast Improvements
// 2.0+: smart cast preserved after variable reassignment
var result: Any = fetchData()
if (result is String) {
// Before: smart cast failed if reassignment was possible
// 2.0: compiler analyzes more accurately
println(result.length)
}
// 2.0+: intersection types for local variables
fun process(value: Any) {
if (value is Comparable<*> && value is Iterable<*>) {
// value smart-cast to Comparable & Iterable
}
}
Compiler Plugin API Changes
- K2 compiler transition changes the compiler plugin API
- Migrate from kapt to KSP (Kotlin Symbol Processing)
Gradle Configuration Changes
// build.gradle.kts
// To explicitly use K1 compiler (for compatibility issues)
kotlin {
compilerOptions {
// Temporary workaround for K2 issues
apiVersion.set(KotlinVersion.KOTLIN_1_9)
languageVersion.set(KotlinVersion.KOTLIN_1_9)
}
}
Migration Notes
- Projects using kapt: K2 + kapt has limited support β prioritize KSP migration
- Compile error changes: K2 is stricter; previously passing code may produce errors
- Especially in type inference and overload resolution
- Gradle plugin compatibility:
kotlin-gradle-plugin2.0 requires Gradle 7.6.3+ or 8.x - Build cache is incompatible between K1 and K2 β clean build required on transition
- Some K1-only compiler options have been removed or changed
8. Kotlin 2.0 β 2.1
Guard Conditions in When (Preview)
// 2.1+: guard conditions on when branches (preview β requires -Xwhen-guards)
sealed class Result {
data class Success(val data: List<String>) : Result()
data class Error(val code: Int) : Result()
}
fun handle(result: Result) {
when (result) {
is Result.Success if result.data.isNotEmpty() -> processData(result.data)
is Result.Success -> handleEmptyResult()
is Result.Error if result.code == 404 -> handleNotFound()
is Result.Error -> handleGenericError(result.code)
}
}
- Additional conditions can be specified with
ifafter pattern matching - Eliminates nested
if-elsefor improved readability
Non-Local Break and Continue (Preview)
// 2.1+: break/continue in inline function lambdas (preview β requires -Xnon-local-break-continue)
fun processItems(items: List<Item>) {
for (group in groups) {
group.forEach { item ->
if (item.isInvalid()) continue // 2.1+: continues outer for loop
if (item.isTerminal()) break // 2.1+: breaks outer for loop
process(item)
}
}
}
Multi-Dollar String Interpolation
// 2.1+: multi-dollar interpolation (experimental β requires -Xmulti-dollar-interpolation)
// $$ prefix: two dollar signs trigger interpolation, single $ is literal
val jsonSchema = $$"""
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/product.schema.json",
"title": "$${simpleName ?: "unknown"}",
"type": "object"
}
"""
// $schema, $id β literal (single $)
// $${simpleName} β interpolated (double $$)
$$"..."prefix means$$triggers interpolation, single$is treated as literal- Reduces escaping in strings with frequent
$literals (JSON schemas, regex, scripts) - Requires compiler option
-Xmulti-dollar-interpolationin Kotlin 2.1 (stable in 2.2)
Migration Notes
- Guard conditions (preview) can replace existing
when+ innerifpatterns β adopt after stabilization - Non-local break/continue (preview) only works in
inline funlambdas β adopt after stabilization - Multi-dollar strings require
-Xmulti-dollar-interpolationopt-in in 2.1 (stable in 2.2)
9. Kotlin 2.1 β 2.2 (June 2025)
Stable Features (Promoted from Preview)
- Guard conditions in
whenβ no longer requires compiler flag - Non-local
breakandcontinuein inline functions β now stable - Multi-dollar string interpolation β no longer requires
-Xmulti-dollar-interpolation
Context Parameters (Preview)
// 2.2+: implicit context dependencies (preview β requires -Xcontext-parameters)
context(logger: Logger)
fun processOrder(order: Order) {
logger.info("Processing order: ${order.id}")
}
// Caller provides context
with(myLogger) {
processOrder(order)
}
- Replaces the older experimental "context receivers" feature
- Context parameters are NOT receivers β access members by name, not implicitly
- Enable with
-Xcontext-parameterscompiler option
Context-Sensitive Resolution (Preview)
// 2.2+: omit type name when expected type is known (preview)
enum class Problem { CONNECTION, TIMEOUT, AUTH }
fun handle(problem: Problem) = when (problem) {
CONNECTION -> reconnect() // Instead of Problem.CONNECTION
TIMEOUT -> retry()
AUTH -> reauth()
}
- Enable with
-Xcontext-sensitive-resolutioncompiler option
Nested Type Aliases (Beta)
// 2.2+: type aliases inside classes
class Graph {
typealias NodeSet = Set<Node>
typealias EdgeList = List<Edge>
fun traverse(visited: NodeSet): EdgeList = ...
}
- Enable with
-Xnested-type-aliasescompiler option
JVM Default Interface Methods
// 2.2+: interface functions compiled to JVM default methods by default
// Configure via compilerOptions:
kotlin {
compilerOptions {
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY // No bridge methods
}
}
| Mode | Behavior |
|---|---|
ENABLE |
Default methods + compatibility bridges (default) |
NO_COMPATIBILITY |
Default methods only (recommended for new code) |
DISABLE |
Previous behavior (DefaultImpls classes) |
@JvmExposeBoxed for Value Classes
// 2.2+: expose boxed value class variants to Java
@JvmInline
@JvmExposeBoxed
value class UserId(val value: Long)
// Java can now use: new UserId(42L) β previously inaccessible
Standard Library Stabilizations
Base64API promoted to stable (encode/decode with Default, UrlSafe, Mime schemes)HexFormatAPI promoted to stable
Removals and Deprecations
- Language versions 1.6 and 1.7 no longer supported
kotlin-android-extensionsplugin removed β usekotlin-parcelizeand View BindingkotlinOptions {}block in Gradle raised to error β usecompilerOptions {}
Migration Notes
- Guard conditions, non-local break/continue, multi-dollar strings now stable β safe to adopt
- Replace
kotlinOptions {}withcompilerOptions {}in all build files - Remove
-Xwhen-guards,-Xnon-local-break-continue,-Xmulti-dollar-interpolationflags - Context parameters are still preview in Kotlin 2.3 β evaluate but do not use in production yet
- Nested type aliases and improved
whenexhaustiveness checks are now stable - If using Java interop with value classes, consider
@JvmExposeBoxed
10. Kotlin 2.2 β 2.3 (December 2025)
Kotlin 2.3.0 was released on December 16, 2025.
Stable Features (Promoted from Beta/Experimental)
The following features have graduated to Stable in Kotlin 2.3.0:
- Support for nested type aliases β type aliases can now be declared inside classes
- Data-flow-based exhaustiveness checks for
whenexpressions β improved compile-time safety
Features Enabled by Default
Several features are now enabled by default without requiring compiler flags.
Explicit Backing Fields
// 2.3+: declare explicit backing field with different type
class Counter {
val count: Int
field = AtomicInteger(0) // Backing field is AtomicInteger
get() = field.get()
fun increment() {
count.field.incrementAndGet() // Direct access to backing field
}
}
Unused Return Value Checker
- Compiler warns when function return values are not used
- Helps catch bugs where results are accidentally discarded
Java 25 Support (JVM)
- Kotlin/JVM now supports Java 25 as compilation target
- Bytecode generation for Java 25 features
Gradle 9.0 Compatibility
- Kotlin Gradle Plugin now compatible with Gradle 9.0
- New API for registering generated sources
Kotlin/Native Improvements
- Swift export improvements for better interoperability
- Faster release build tasks
- C and Objective-C library import now in Beta
Kotlin/Wasm Improvements
- Fully qualified names and new exception handling proposal enabled by default
- New compact storage for Latin-1 characters
Standard Library
- Stable time tracking functionality
- Improved UUID generation and parsing
Migration Notes
- Review code for unused return values β new warnings may appear
- Update Gradle to 9.0 if using latest Kotlin features
- Java 25 target available for projects requiring latest JVM features
11. Migration Checklist
Before Version Upgrade
- [ ] Check deprecated API warnings in current project (
-Werrorflag to treat warnings as errors) - [ ] Verify Gradle plugin compatibility (kotlin-gradle-plugin, Spring Boot plugin)
- [ ] Verify dependency library Kotlin version compatibility
- [ ] Verify compiler plugin compatibility (kapt, allopen, noarg)
Upgrade Procedure
- Change Kotlin version in
gradle.properties - Run clean build (
./gradlew clean build) - Review compile errors and warnings β replace deprecated APIs
- Run full test suite
- Validate in CI pipeline
After Version Upgrade
- [ ] Replace newly deprecated APIs (prepare for future removal)
- [ ] Coordinate new feature adoption with team (code style consistency)
- [ ] Remove explicit
languageVersion,apiVersionsettings (use new version defaults)
12. Deprecated API Replacement Guide
| Deprecated | Replacement | Since |
|---|---|---|
inline class |
@JvmInline value class |
1.5 |
Enum.values() |
Enum.entries |
1.9 |
object (sealed subtype) |
data object |
1.9 |
kotlinOptions { jvmTarget = "..." } |
kotlin { jvmToolchain(N) } |
1.8 |
@Experimental |
@RequiresOptIn |
1.7 |
kotlin-android-extensions plugin |
View Binding / Compose | 1.8 |
kapt |
KSP | 2.0 |
kotlin.experimental.ExperimentalTypeInference |
Not needed (builder inference stable) | 1.6 |
capitalize() / decapitalize() |
replaceFirstChar { ... } |
1.5 |
Iterable.sumBy { } |
Iterable.sumOf { } |
1.5 |
mapNotNull { }.first() |
firstNotNullOf { } |
1.5 |
x..y - 1 or x until y |
x..<y |
1.9 |
kotlinOptions {} (Gradle) |
compilerOptions {} |
2.2 |
kotlin-android-extensions plugin |
kotlin-parcelize + View Binding |
2.2 |
Context receivers (context(T)) |
Context parameters (context(t: T)) |
2.2 |
13. Compiler Options Reference
Recommended Compiler Settings
// build.gradle.kts
kotlin {
jvmToolchain(21)
compilerOptions {
allWarningsAsErrors.set(true) // Treat warnings as errors
freeCompilerArgs.addAll(
"-Xjsr305=strict", // Strict JSR-305 null annotation handling
"-opt-in=kotlin.RequiresOptIn" // Allow opt-in API usage
)
}
}
Kotlin 2.2+ Experimental Feature Flags
// build.gradle.kts β opt-in to preview features
kotlin {
compilerOptions {
freeCompilerArgs.addAll(
"-Xcontext-parameters", // Context parameters (2.2 preview)
"-Xcontext-sensitive-resolution", // Context-sensitive resolution (2.2 preview)
"-Xnested-type-aliases", // Nested type aliases (2.2 beta)
)
}
}
Flexible Compiler Version (2.2+)
// Use a different compiler version than the Gradle plugin version
kotlin {
compilerVersion.set("2.3.0") // Use newer compiler with older plugin
}
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.