Skip to content
Published on

モダン Kotlin 2026 — Kotlin 2.1 / K2 コンパイラ / Compose Multiplatform / KMP 2.1 / Ktor 3 / Spring Boot Kotlin 深掘りガイド

Authors

プロローグ — 2026 年の Kotlin はもう「Android の言語」ではない

2017 年の Google I/O で Android 公式言語になった日から、Kotlin はしばらく「Android のための Java 代替」として生きてきた。サーバ陣営は Java 17、21 を使えばよかったし、マルチプラットフォームはアルファからベータへゆっくりと進んでいた。

2026 年 5 月の風景は当時とまったく違う。

  • Kotlin 2.1(2024 年 11 月)が K2 コンパイラをデフォルトに昇格させた。IDE も K2 モードがデフォルトで、クリーンビルドは K1 比平均 1.8 倍速い。
  • Kotlin 2.2(2025 年 3 月)が context parameters — 旧 context receivers の正式な後継 — を導入。戻り値の型を汚さずに、関数シグネチャに依存性を結びつけられる。
  • Compose Multiplatform 1.7(2024 年 10 月)が iOS を stable と宣言。Android・iOS・デスクトップ・Web(Wasm)を 1 コードベースで描く。
  • KMP(Kotlin Multiplatform)2.1 は production-ready を超え、メルカリ・マクドナルドなどがコア SDK を KMP に書き換えている。
  • Ktor 3(2024 年 10 月)が WebAssembly ターゲットを追加し、kotlinx-io ベースで書き直されてスループットが平均 90% 上昇。
  • Spring Boot 3.5 は Kotlin を一級言語扱い。coroutine サポートは成熟、Kotlin DSL ビルドが標準。

本稿は「Kotlin を見始めたばかり」から「2026 年の本番でどう動くか全部見たい」までを一気に整理する。言語・コンパイラ・UI・サーバ・ライブラリ陣営・国内外事例を漏らさず押さえる。


1 章 · 2026 年のモダン Kotlin — Kotlin 2.x 時代の座標

まず座標を取ろう。2026 年 5 月時点の Kotlin フルスタックは次のとおり。

                +----------------------------------------+
                |             Kotlin 2.2.x               |
                |  (K2 compiler default, JVM/JS/Wasm)    |
                +----------------------------------------+
                              |
          +-------------------+--------------------+
          |                   |                    |
          v                   v                    v
   [JVM バックエンド]   [マルチプラットフォーム]  [Native / Wasm]
   Spring Boot 3.5     KMP 2.1 + CMP 1.7        Kotlin/Native
   Ktor 3              iOS/Android/Desktop      Kotlin/Wasm
   gRPC-Kotlin         Web (Wasm)               (Ktor 3 server)
          |                   |                    |
          +-------------------+--------------------+
                              |
                              v
                  +-------------------------+
                  |   kotlinx ライブラリ    |
                  | coroutines / Flow       |
                  | serialization / io      |
                  | datetime / atomicfu     |
                  +-------------------------+
                              |
                              v
              +---------------+----------------+
              |               |                |
              v               v                v
         [DI/FP/ORM]    [静的解析/テスト]    [ビルド]
         Koin / Arrow   Detekt / Konsist     Gradle Kotlin DSL
         Exposed        Kotest / MockK       Maven Kotlin plugin

要点整理:

  • 言語自体は 2 年に 1 度 major(1.x → 2.x)、半年に 1 度 minor。
  • マルチプラットフォームはもう「実験」ではない。iOS stable がゲームチェンジャー。
  • サーバ陣営は Spring Boot と Ktor の両方が健在。
  • ライブラリ陣営(Koin/Arrow/Exposed/Kotest/MockK)が分厚く、Kotlin らしい流儀でフルスタックを組める。

この座標の上で、各エリアを一つずつ深掘りする。


2 章 · Kotlin 2.1(2024.11)— K2 デフォルト + マルチドル文字列

Kotlin 2.0 が K2 コンパイラを stable に昇格した最初のリリースなら、2.1 はそれを 全員に強制したリリース。JetBrains は同時に IntelliJ IDEA の K2 モードもデフォルトにし、K1 フォールバックは 2025 年末までしかサポートしない。

2.1 のヘッドライン機能:

  1. K2 コンパイラがデフォルト-language-version 2.0 以上はすべて K2。
  2. マルチドル($$$$$)文字列補間 — テンプレートや LaTeX を扱うときの $ エスケープ地獄から解放。
  3. when 式の guard conditionwhen (x) { is Foo if x.bar > 10 -> ... } で分岐に追加条件をインラインで書ける。
  4. .kotlin キャッシュディレクトリ — Gradle の .gradle のように、ビルド成果物をプロジェクト直下に集約。
  5. non-local break / continue — inline ラムダ内からも外側のループを抜けられる(preview)。

マルチドル文字列は本当に楽になる。

// Kotlin 2.1: $$"..."$$ 内では単一の $ はリテラル、$$ で補間開始
val template = $$"""
    Hello, $$name!
    Your balance is $1000 (USD).
    JSON: {"value": "$$value"}
"""

従来は毎回 "\$1000" のように $ をエスケープするか、raw string にさらに別の文字列を継ぎ足す必要があった。これが一行で終わる。

when guard も頻出。

sealed interface Order
data class Pending(val amount: Int) : Order
data class Paid(val amount: Int, val txId: String) : Order

fun handle(order: Order) = when (order) {
    is Pending if order.amount > 10_000 -> "high-value pending"
    is Pending -> "pending"
    is Paid -> "paid: ${order.txId}"
}

K1 では is Pending 分岐の中で if をネストする必要があり、その瞬間に exhaustive チェックが効かなくなった。今や一行で終わり、網羅性も維持される。


3 章 · Kotlin 2.2(2025.3)— context parameters

Kotlin 2.2 のヘッドラインは context parameters — 1.7 からベータで動いていた「context receivers」の正式後継。

何を解決するのか

関数に 暗黙的に 依存性を注入したいことがある。例えば:

// 従来: 毎回 logger を明示的に受け取る
fun calculate(x: Int, y: Int, logger: Logger): Int {
    logger.info("calc $x + $y")
    return x + y
}

// あるいはクラスで包む
class Calculator(private val logger: Logger) {
    fun calculate(x: Int, y: Int): Int { ... }
}

context parameters は シグネチャに依存性を宣言しつつ、呼び出し側は明示的に渡さなくてよい 中間路線。

// Kotlin 2.2 context parameters
context(logger: Logger)
fun calculate(x: Int, y: Int): Int {
    logger.info("calc $x + $y")
    return x + y
}

// 呼び出し側: スコープに Logger を持ち込めば自動注入
fun main() {
    with(MyLogger()) {
        val result = calculate(1, 2)  // logger 明示なし
    }
}

context receivers と何が違うか

旧 1.x の context(Logger)名前が無かった。同じ型のコンテキストが二つ入ると衝突。2.2 の context(logger: Logger)名前付き。Arrow の effect ライブラリ、Compose の CompositionLocal、structured logging が軒並みこれを基盤に書き直されている。

実例: Arrow の raise

import arrow.core.raise.Raise

context(raise: Raise<String>)
fun parsePositive(s: String): Int {
    val n = s.toIntOrNull() ?: raise.raise("not a number: $s")
    if (n <= 0) raise.raise("must be positive: $n")
    return n
}

// 呼び出し側
fun main() {
    val result = either {
        val n = parsePositive("42")
        n * 2
    }
    // result: Either.Right(84)
}

Either<E, A> のような結果型を露出させずにエラーチャネルを合成できる。Scala の ZIO や Haskell の monad transformer 流の effect system を Kotlin らしく落とし込んだ解。


4 章 · K2 コンパイラ — 何が速くなり、何が変わるのか

K2 はコンパイラのフロントエンドを 全面書き直し した成果物。K1 は 2010 年代初頭の設計で、型推論・smart cast・generic inference のコードが複数のパスに散らばっていた。K2 は FIR(Frontend IR) という単一 IR に統一した。

体感効果:

指標K1K2備考
クリーンビルド1.0x1.6 〜 1.9x 高速大規模プロジェクトほど差が大きい
インクリメンタルビルド1.0x2.0 〜 2.4x 高速キャッシュ / invalidation 改善
IDE highlight1.0x1.5x 高速smart cast / 推論キャッシュ
ピークメモリ1.0x約 0.7xFIR 統合で重複削減

数値は JetBrains 公式ベンチマーク + メルカリ・Square のマイグレーション報告の平均。

Smart cast がさらに賢くなった

K1 は複雑な分岐で一貫した型の絞り込みが苦手だった。

fun process(value: Any?) {
    if (value is String || value is Int) {
        // K1: value は依然 Any?
        // K2: value は String | Int(intersection)
        println(value.hashCode())  // K2 なら問題なく呼べる
    }
}

K2 は val の遅延初期化 パターンも認識する。

class Holder {
    val data: List<Int>
    init {
        val tmp = mutableListOf<Int>()
        tmp.add(1); tmp.add(2)
        data = tmp.toList()  // K2: OK
    }
}

コンパイラプラグイン API の安定化

Compose コンパイラプラグイン、kotlinx-serialization、Arrow の raise プラグインがすべて K2 FIR ベースで書き直された。結果としてビルド時間が短縮、IDE インデックスも安定。

マイグレーションチェックリスト

  • kotlin.languageVersion = "2.1" に上げる。
  • kotlin.compiler.execution.strategy = in-process(Gradle daemon)。
  • Compose プロジェクトは compose-compiler-gradle-plugin(2024 年から JetBrains が直接配布)を使う。
  • -Xuse-fir-lt のような旧フラグを削除。

5 章 · Compose Multiplatform — iOS stable!

2024 年 10 月の Compose Multiplatform 1.7 は本当に大きな節目だった。iOS ターゲットが stable に昇格。それまで Compose-Android は本番品質だったが iOS はベータ。1.7 以降は本番 OK。

1.7 ヘッドライン(2024.10)+ 1.7+ の積み増し

  • iOS stable — UIViewController 統合、テキスト入力、アクセシビリティ、ダークモードすべて stable。
  • Resource API 1.0 — drawable / string / font を Res.drawable.icon のような型安全プロキシで参照。
  • adaptive layoutsWindowSizeClass API。スマホ / タブレット / フォルダブルの分岐。
  • Skia 0.8 → 0.10 — テキスト描画品質、GPU メモリ改善。
  • ホットリロード(desktop) — Java 21 の enhanced class redefinition を活用。デザインループが本当に速い。

1 画面、4 プラットフォーム

// commonMain/HelloApp.kt
@Composable
fun HelloApp(name: String) {
    MaterialTheme {
        Column(modifier = Modifier.padding(16.dp)) {
            Text("Hello, $name!", style = MaterialTheme.typography.headlineSmall)
            Spacer(Modifier.height(8.dp))
            Button(onClick = { /* ... */ }) {
                Text("Tap me")
            }
        }
    }
}

このまま Android、iOS、macOS / Windows / Linux デスクトップ、そして(実験的に)Web Wasm で動く。プラットフォーム固有部分だけ expect / actual に切り出す。

// commonMain
expect fun platformName(): String

// androidMain
actual fun platformName(): String = "Android ${Build.VERSION.RELEASE}"

// iosMain
actual fun platformName(): String =
    UIDevice.currentDevice.systemName + " " + UIDevice.currentDevice.systemVersion

Compose vs SwiftUI 比較(2026)

項目Compose Multiplatform 1.7+SwiftUI
プラットフォームAndroid / iOS / Desktop / WebApple のみ
言語KotlinSwift
コード共有100%(UI まで)0%(他プラットフォーム)
iOS パフォーマンス95%(スクロール / アニメーション一部 gap 残)100% native
ネイティブウィジェットUIViewControllerRepresentable 風の interop自然
ビルド時間KMP のビルドオーバーヘッドありXcode 標準

選び方:

  • iOS のみ → SwiftUI。
  • Android + iOS、デザイン一貫性が重要 → CMP。
  • 「Apple の新 API をその日から使う」 → SwiftUI。

6 章 · KMP 2.1 — production-ready

Compose Multiplatform が UI なら、KMP(Kotlin Multiplatform)ロジック共有。ビジネスロジック、ネットワーキング、ディスクキャッシュ、ドメインモデルを Kotlin で 1 度書いて、Android・iOS・サーバ・Web で使い回す。

KMP モジュール構成

shared/
  build.gradle.kts
  src/
    commonMain/kotlin/    <- 全プラットフォーム共通コード
    androidMain/kotlin/   <- Android 専用
    iosMain/kotlin/       <- iOS 専用(Kotlin/Native)
    jvmMain/kotlin/       <- JVM サーバ用
    wasmJsMain/kotlin/    <- Wasm Web 用
    commonTest/kotlin/    <- 共通テスト

コアライブラリ

  • kotlinx.coroutines — どのプラットフォームでも同じ API。
  • kotlinx.serialization — JSON / Protobuf / CBOR。multiplatform 標準。
  • Ktor Client — multiplatform HTTP / WebSocket。
  • SQLDelight 2 — 型安全 SQL ドライバ。iOS / Android / JVM / Wasm 対応。
  • Multiplatform Settings — UserDefaults / SharedPreferences / ファイルを抽象化。
  • Decompose / Voyager — multiplatform ナビゲーション。
  • Koin — DI、multiplatform。

iOS 統合 — KMP 成果物を Xcode へ

// shared/build.gradle.kts
kotlin {
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    cocoapods {
        version = "1.0"
        summary = "Shared module for MyApp"
        homepage = "https://example.com"
        ios.deploymentTarget = "15.0"
        framework {
            baseName = "Shared"
            isStatic = false
        }
    }
}
./gradlew :shared:podPublishXcFramework
# 成果物を Xcode プロジェクトの Podfile に追加

Swift 側はそのまま import。

import Shared

let repo = UserRepository()
Task {
    let users = try await repo.fetchUsers()
    print(users)
}

Kotlin の suspend fun は Swift の async throws に自動マッピングされる(2.1 と Kotlin/Native の swift-export 改善のおかげ)。

メルカリ、マクドナルドなどの事例

  • メルカリ — 検索 / 決済のコアロジックを KMP に移行。Android / iOS のバグが 1 か所で直る。
  • マクドナルド — Global Mobile App を KMP ベースで再構築。
  • 国内事例(メルカリ・ZOZO・CyberAgent)は 13 章で詳しく。

7 章 · Ktor 3(2024.10)— WebAssembly ターゲット

Ktor 3 は 2024 年 10 月リリース。「v2 のマイナーアップ」ではなく ほぼ書き直し のメジャー。

ヘッドライン

  1. kotlinx-io ベースに書き直し — okio / java.nio 依存を撤廃。スループット平均 +90%。
  2. WebAssembly ターゲット — Kotlin/Wasm で Ktor サーバが組める。エッジコンピュートのシナリオ。
  3. CIO エンジン HTTP/2 stable — 外部依存なしで HTTP/2 サーバを立てられる。
  4. Server-Sent Events プラグインinstall(SSE)
  5. WebSocket extensions — Compression、ping / pong を再設計。

最小 Ktor 3 サーバ

// build.gradle.kts
plugins {
    kotlin("jvm") version "2.1.0"
    application
}
dependencies {
    implementation("io.ktor:ktor-server-core:3.0.0")
    implementation("io.ktor:ktor-server-cio:3.0.0")
    implementation("io.ktor:ktor-server-content-negotiation:3.0.0")
    implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.0")
}
// src/main/kotlin/Application.kt
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.serialization.Serializable

@Serializable
data class User(val id: Long, val name: String)

fun main() {
    embeddedServer(CIO, port = 8080) {
        install(ContentNegotiation) { json() }
        routing {
            get("/users") {
                call.respond(listOf(User(1, "Alice"), User(2, "Bob")))
            }
        }
    }.start(wait = true)
}

./gradlew run で 8080 から JSON が返る。coroutine ベースなので thread-per-request ではなく、1 スレッドが数千コネクションを捌く。

Spring Boot vs Ktor — どこで何を使うか

項目Ktor 3Spring Boot 3.5 + Kotlin
哲学minimal、opt-in plugins「convention over config」
並行モデルcoroutine nativeWebFlux(reactive)or virtual threads
エコシステム軽量巨大、なんでもある
コールドスタート速いGraalVM native image なら速い
学習コスト短い長いが資料豊富
向きマイクロサービス、エッジ、MVP大型モノリス、エンタープライズ

8 章 · Spring Boot + Kotlin — JVM サーバ

JVM サーバで Kotlin を使うと言われたとき、企業の 70% は依然 Spring Boot。そして Spring チームはそのトラフィックを無視しなかった。Spring Framework 6、Boot 3 系列は Kotlin サポートを「一級」に格上げした。

モダン Spring Boot + Kotlin スタック(2026)

  • Spring Boot 3.5(Java 21 baseline、Kotlin 2.1+)
  • Spring WebFlux + coroutinessuspend 関数をコントローラで直接受ける。
  • R2DBC + Kotlin Coroutines — async DB。
  • Spring Security 6 — Kotlin DSL。
  • Spring AOT + GraalVM native image — コールドスタート 100 ms 未満。

Coroutine コントローラ

@RestController
class UserController(private val repo: UserRepository) {
    @GetMapping("/users/{id}")
    suspend fun getUser(@PathVariable id: Long): UserDto =
        repo.findById(id)?.toDto() ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)

    @GetMapping("/users", produces = [MediaType.APPLICATION_NDJSON_VALUE])
    fun streamUsers(): Flow<UserDto> =
        repo.findAll().map { it.toDto() }
}

suspend fun をそのまま受け取る。WebFlux の Mono / Flux を変換する必要はない。Flow<UserDto> は NDJSON ストリーミングレスポンスへ自動変換。

Coroutine リポジトリ(R2DBC)

interface UserRepository : CoroutineCrudRepository<UserEntity, Long> {
    suspend fun findByEmail(email: String): UserEntity?

    @Query("SELECT * FROM users WHERE created_at > :since")
    fun findRecentUsers(since: Instant): Flow<UserEntity>
}

Kotlin DSL コンフィグ

@Configuration
class AppConfig {
    @Bean
    fun routes(handler: UserHandler) = coRouter {
        accept(MediaType.APPLICATION_JSON).nest {
            GET("/users", handler::list)
            POST("/users", handler::create)
        }
    }

    @Bean
    fun securityFilter(http: ServerHttpSecurity): SecurityWebFilterChain =
        http {
            authorizeExchange {
                authorize("/public/**", permitAll)
                authorize(anyExchange, authenticated)
            }
            oauth2ResourceServer { jwt {} }
        }
}

http { ... } が Kotlin DSL。XML / Java config のボイラープレートが消える。


9 章 · kotlinx.coroutines + Flow — async モデル

Kotlin の async モデルは 2 軸。coroutines(一度完結する非同期処理)と Flow(時間軸を流れる値のストリーム)。

Coroutines 基本

import kotlinx.coroutines.*

suspend fun fetchUser(id: Long): User { /* HTTP 呼び出し */ }
suspend fun fetchOrders(userId: Long): List<Order> { /* HTTP 呼び出し */ }

suspend fun loadProfile(id: Long): Profile = coroutineScope {
    val userDeferred = async { fetchUser(id) }
    val ordersDeferred = async { fetchOrders(id) }
    Profile(userDeferred.await(), ordersDeferred.await())
}

coroutineScope 内で 2 つのタスクが並列に走り、どちらかが失敗すればもう一方も cancel — structured concurrency

Flow — cold stream

fun userUpdates(userId: Long): Flow<UserEvent> = flow {
    while (currentCoroutineContext().isActive) {
        val event = pollEvent(userId)
        if (event != null) emit(event)
        delay(1000)
    }
}

// 消費側
suspend fun watchUser() {
    userUpdates(42L)
        .filter { it.type == EventType.UPDATED }
        .map { it.toDto() }
        .take(10)
        .collect { println(it) }
}

Flow は cold.collect を呼ぶたびに最初から再実行。hot stream が要るなら SharedFlow / StateFlow

StateFlow — UI state holder

class UserViewModel(private val repo: UserRepository) {
    private val _state = MutableStateFlow<UserState>(UserState.Loading)
    val state: StateFlow<UserState> = _state.asStateFlow()

    fun load(id: Long) {
        viewModelScope.launch {
            _state.value = UserState.Loading
            _state.value = runCatching { repo.findById(id) }
                .fold(
                    onSuccess = { UserState.Loaded(it) },
                    onFailure = { UserState.Error(it.message ?: "") }
                )
        }
    }
}

Compose では val state by viewModel.state.collectAsState() で受けてそのまま描画。

Coroutine デバッガ

IntelliJ の Debug メニュー → Coroutines Dump。どの coroutine がどこで suspended かをツリーで表示。Java スレッドダンプとは比較にならない便利さ。


10 章 · Detekt / Konsist — 静的解析 + アーキテクチャテスト

Kotlin のコード品質を自動で守る 2 つの道具。

Detekt — コードスメル静的解析

// build.gradle.kts
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.7"
}

detekt {
    toolVersion = "1.23.7"
    config.setFrom("$rootDir/detekt.yml")
    buildUponDefaultConfig = true
}

detekt.yml 抜粋:

complexity:
  CyclomaticComplexMethod:
    threshold: 15
  LongMethod:
    threshold: 60
naming:
  FunctionNaming:
    functionPattern: '[a-z][a-zA-Z0-9]*'
style:
  MagicNumber:
    ignoreNumbers: ['-1', '0', '1', '2']

CI 連携:

./gradlew detekt
# 結果: build/reports/detekt/detekt.html

K2 互換は 1.23 から stable、2025 年半ばの 1.24 からは K2 専用解析ルール(smart-cast hint、sealed exhaustive など)が追加された。

Konsist — アーキテクチャテスト

ArchUnit の Kotlin 版。レイヤ依存、命名規則、アノテーション利用 をユニットテストとして検証する。

// src/test/kotlin/ArchitectureTest.kt
import com.lemonappdev.konsist.api.Konsist
import com.lemonappdev.konsist.api.verify.assertTrue

class ArchitectureTest {
    @Test
    fun `repositories should not depend on controllers`() {
        Konsist.scopeFromProject()
            .classes()
            .withNameEndingWith("Repository")
            .assertTrue { repo ->
                repo.containingFile.imports
                    .none { it.name.contains("controller") }
            }
    }

    @Test
    fun `all use cases end with UseCase`() {
        Konsist.scopeFromPackage("..usecase..")
            .classes()
            .assertTrue { it.name.endsWith("UseCase") }
    }
}

./gradlew test で自動実行。新人が「controller から repository を直接呼び出す」PR を出すと CI が赤くなる。

Detekt が「ファイル内のコード品質」、Konsist が「プロジェクト全体の構造」。両者は補完関係。


11 章 · Koin / Arrow / Exposed / Kotest / MockK — ライブラリ陣営

Java の Spring / Hibernate / Mockito に対応する「Kotlin native」ライブラリたち。

Koin — DSL ベースの DI

val appModule = module {
    single<UserRepository> { UserRepositoryImpl(get()) }
    single<HttpClient> {
        HttpClient(CIO) {
            install(ContentNegotiation) { json() }
        }
    }
    viewModel { UserViewModel(get()) }
}

fun main() {
    startKoin {
        modules(appModule)
    }
    // ...
}

リフレクションなし。Dagger のような compile-time generation もなし。KMP 互換。Compose Multiplatform でもそのまま使われる。

Arrow — Kotlin の FP 標準

import arrow.core.Either
import arrow.core.raise.either

sealed interface UserError {
    object NotFound : UserError
    data class Invalid(val reason: String) : UserError
}

fun parseAge(s: String): Either<UserError, Int> = either {
    val n = s.toIntOrNull() ?: raise(UserError.Invalid("not a number: $s"))
    if (n < 0 || n > 150) raise(UserError.Invalid("age out of range: $n"))
    n
}

fun loadUser(id: Long): Either<UserError, User> = either {
    val user = repo.find(id) ?: raise(UserError.NotFound)
    user
}

fun process(id: Long, ageStr: String): Either<UserError, Result> = either {
    val user = loadUser(id).bind()
    val age = parseAge(ageStr).bind()
    Result(user, age)
}

Either<L, R>raise DSL の組み合わせで throw なしにエラーを合成。2.2 の context parameters と組み合わせると effect system が完成する。

Exposed — 型安全 SQL DSL

object Users : LongIdTable("users") {
    val name = varchar("name", 100)
    val email = varchar("email", 200).uniqueIndex()
    val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
}

transaction {
    val newId = Users.insertAndGetId {
        it[name] = "Alice"
        it[email] = "alice@example.com"
    }

    val users = Users
        .selectAll()
        .where { Users.name like "A%" }
        .orderBy(Users.createdAt to SortOrder.DESC)
        .limit(10)
        .map { it[Users.name] to it[Users.email] }
}

JPA より軽量で、Hibernate の lazy-loading の罠もない。R2DBC アダプタで coroutine 環境でも動く(1.0 から stable)。

Kotest — BDD スタイルのテスト

class UserSpec : StringSpec({
    "user with valid email should be created" {
        val u = User("alice@example.com")
        u.isValid shouldBe true
    }

    "negative age should throw" {
        shouldThrow<IllegalArgumentException> {
            User("a@b", age = -1)
        }
    }
})

class PropertySpec : StringSpec({
    "reverse twice is identity" {
        checkAll<List<Int>> { list ->
            list.reversed().reversed() shouldBe list
        }
    }
})

JUnit 互換、式ベースの assertion、property-based testing(QuickCheck 流)を内蔵。multiplatform。

MockK — pure Kotlin mocking

class UserServiceTest : StringSpec({
    val repo = mockk<UserRepository>()
    val service = UserService(repo)

    "should return user from repo" {
        coEvery { repo.findById(1L) } returns User(1, "Alice")

        val u = service.getUser(1L)
        u.name shouldBe "Alice"

        coVerify(exactly = 1) { repo.findById(1L) }
    }
})

Mockito が扱えない objectcompanion object、トップレベル関数、suspend 関数を全部 mock できる。


12 章 · Gradle Kotlin DSL — ビルド標準

2026 年時点で 新規 Kotlin プロジェクトの 99% は Gradle Kotlin DSLbuild.gradle.kts)。Groovy DSL はレガシー。

最小 build.gradle.kts

plugins {
    kotlin("jvm") version "2.1.0"
    kotlin("plugin.serialization") version "2.1.0"
    application
}

group = "com.example"
version = "0.1.0"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
    testImplementation(kotlin("test"))
    testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
}

application {
    mainClass.set("com.example.AppKt")
}

kotlin {
    jvmToolchain(21)
}

tasks.test {
    useJUnitPlatform()
}

Multi-module + version catalog

gradle/libs.versions.toml

[versions]
kotlin = "2.1.0"
coroutines = "1.9.0"
ktor = "3.0.0"

[libraries]
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }

各モジュールの build.gradle.kts

plugins {
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.ktor)
}
dependencies {
    implementation(libs.ktor.server.core)
    implementation(libs.ktor.server.cio)
    implementation(libs.kotlinx.coroutines)
}

バージョンを 1 か所で管理。30 モジュール超のプロジェクトで真価を発揮する。

configuration cache + build cache

# gradle.properties
org.gradle.configuration-cache=true
org.gradle.caching=true
org.gradle.parallel=true
kotlin.incremental=true
kotlin.code.style=official

設定キャッシュは 8.x から stable。ビルド時間は明確に 2〜5 倍速くなる(特にインクリメンタル)。

Maven は死んだか?

否。Spring Boot エンタープライズ + 既存 Maven 資産の大きい組織 は依然 Maven Kotlin plugin を使う。

<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>2.1.0</version>
    <configuration>
        <jvmTarget>21</jvmTarget>
        <compilerPlugins>
            <plugin>spring</plugin>
            <plugin>jpa</plugin>
        </compilerPlugins>
    </configuration>
</plugin>

機能的には同等。Gradle より遅いが安定。


13 章 · 韓国 / 日本 — カカオ、トス、メルカリ、ZOZO、CyberAgent

韓国

カカオ(Android 全社 Kotlin)

  • KakaoTalk Android コードベースの 95% 以上が Kotlin。新規 Java コード禁止。
  • 100 以上のモジュールで multi-module、Gradle Kotlin DSL + version catalog。
  • 社内で Detekt + カカオ独自ルール(KakaoCodeStyle)を配布。
  • Compose 移行進行中(2026 年時点、新規画面は 100% Compose)。

トス(Toss)— Server-side Kotlin の先駆者

  • バックエンドの新規マイクロサービスは 100% Kotlin + Spring Boot。200 以上のサービスが Kotlin。
  • Coroutine + WebFlux + R2DBC + Arrow の組み合わせが標準。
  • 社内 Kotlin DSL ライブラリ(tossopen-kotlin-commons)多数。
  • トス技術ブログに「Spring の Kotlin マイグレーション」「なぜ Java ではなく Kotlin か」シリーズが多数。

LINE(NAVER)— Kotlin Multiplatform ケース

  • LINE メッセンジャーのクライアント SDK の一部を KMP で共有。2026 年時点で本番一部導入。

日本

メルカリ — KMP の先駆者

  • 検索 / 決済のコア SDK を KMP で再構築。Android / iOS のバグ修正が 1 か所で済む。
  • 2025 年発表:「共有ビジネスロジックの 60% を KMP に移行した」。
  • Compose Multiplatform 1.7+ を評価中。

ZOZOTOWN(ZOZO)

  • Android はフル Kotlin。サーバ一部 Kotlin(Spring Boot + Coroutines)。
  • 社内 "Kotlin Guild" — 隔週勉強会、ライブラリ共有。

CyberAgent(AbemaTV、FRIDAY ほか)

  • Android 新規プロジェクトは 100% Kotlin。
  • AbemaTV Android — Compose + Coroutines + Koin + Coil。
  • 一部マイクロサービスで Ktor 3 を導入(ストリーミングメタデータサービス)。

DeNA / GREE / 楽天

  • 同様パターン:Android フル Kotlin、サーバ部分的に Kotlin。

共通パターン

韓国・日本の大企業すべてに共通:

  1. Android は 100% Kotlin が既に完了。
  2. サーバは 新規マイクロサービスから Kotlin へ移行。
  3. KMP は コア SDK 中心の選択的導入(アプリ全体 KMP はまだ稀)。
  4. 社内コーディングガイドは Detekt ベース + 自社ルール
  5. 採用市場:Kotlin 経験者 = Android + Server 両面に魅力。

14 章 · 誰が Kotlin を選ぶべきか — Android / KMP / JVM サーバ / FP

最後に意思決定マトリクス。「Kotlin が正解か」シナリオ別の答え。

「Android アプリを書く」

Kotlin がデフォルト。他の選択肢が事実上ない。

  • Android Studio が Kotlin 優先。
  • Jetpack Compose の正式言語。
  • Google の新ライブラリ / ドキュメントはすべて Kotlin 優先。

「iOS + Android 両方、1 チームで」

KMP + Compose Multiplatform を検討。

条件:

  • iOS の新 API をその日から使う必要がない(SwiftUI / AppKit の新機能を 1〜2 か月遅れで OK)。
  • ビジネスロジックが両プラットフォームで同一。
  • iOS デザイナーが「これは正確に iOS-native でなければ」と固執しない。

3 つすべて OK なら KMP。1 つでも NG ならネイティブ 2 本立て。

「Spring Boot 環境で JVM サーバを書く」

Kotlin が Java よりほぼ全方面で優位。

  • Null safety = NPE をコンパイル時に潰す。
  • Coroutines = CompletableFuture より圧倒的に読みやすい。
  • Data class = Lombok 不要。
  • Spring 6 / Boot 3.5 = Kotlin 一級サポート。

例外:チームの 80% が Java しか知らない → 段階導入(テストコードから、新規マイクロサービスから)。

「マイクロサービスを軽量に、GraalVM native image で」

Ktor 3 + Kotlin/Native、または Ktor 3 + GraalVM。

  • コールドスタート 100 ms 未満。
  • メモリ 50 MB 未満。
  • ラムダ / エッジ関数にぴったり。

「関数型プログラミングを本気で」

Kotlin + Arrow。ただし学習曲線は急。

  • Either / IO / context parameters / raise DSL の組み合わせ。
  • Scala や Haskell ほど深くはないが、JVM 陣営における「産業向き FP」の sweet spot。

Scala が依然優位。Kotlin は補助。

  • Spark Kotlin API は存在するが Scala ほど成熟していない。
  • ただし KEDB(Kotlin Embedded DB)など新興ツールは Kotlin 優先。

「Android でも JVM でもない」

→ Kotlin は別に来なくてよい。JS / Wasm ターゲットはあるが、その領域では TypeScript / Rust がデフォルト。


参考 / References

Kotlin 言語・コンパイラ公式

Compose Multiplatform / KMP

Ktor / Spring

kotlinx ライブラリ

静的解析・テスト・DI・FP・ORM

Gradle / ビルド

企業ケース

締めくくり。Kotlin 2.x はもはや「Java より少し良い Android 用言語」ではない。コンパイラ(K2)、UI(Compose Multiplatform)、マルチプラットフォーム(KMP)、サーバ(Spring / Ktor)、ライブラリ陣営(Koin / Arrow / Exposed / Kotest) が揃って stable に並んだ、2026 年 JVM 陣営のデフォルト言語である。本稿がその座標を取る助けになれば。