Kotlin

Kotlin完全ガイド — モダンなJVM言語の全容

はじめに

Kotlin(コトリン)は、JetBrains社が2011年に開発を開始し、2016年にバージョン1.0がリリースされたモダンなプログラミング言語である。2017年にGoogleがAndroidの公式開発言語として採用し、2019年にはAndroid開発の推奨言語(Kotlin-first)となった。

Kotlinは「実用的、簡潔、安全、そしてJavaとの100%相互運用可能」を設計哲学としている。JVM上で動作するだけでなく、JavaScript(Kotlin/JS)やネイティブコード(Kotlin/Native)にもコンパイル可能なマルチプラットフォーム言語である。

本記事では、Kotlinの基本構文、型システム、オブジェクト指向、関数型プログラミング、コルーチン、DSL構築、マルチプラットフォーム開発、そしてサーバーサイド/Android開発の実践まで、Kotlinの全容を体系的に解説する。


第1章: Kotlinの基本

1.1 Kotlinの特徴

特徴説明
Null安全型システムレベルでNullPointerExceptionを防止
簡潔な構文ボイラープレートコードの大幅削減
Java相互運用既存Javaコードとの100%互換性
関数型プログラミング高階関数、ラムダ式、イミュータブルコレクション
コルーチン軽量な非同期処理フレームワーク
型推論明示的な型宣言の省略が可能
データクラスPOJO/DTOの自動生成
拡張関数既存クラスへのメソッド追加(継承不要)
スマートキャスト型チェック後の自動キャスト
マルチプラットフォームJVM, JS, Native, Wasmへのコンパイル

1.2 開発環境のセットアップ

# SDKMANによるインストール
$ sdk install kotlin

# Gradleプロジェクトの初期化
$ mkdir my-kotlin-project && cd my-kotlin-project
$ gradle init --type kotlin-application --dsl kotlin

# コマンドラインでのコンパイルと実行
$ kotlinc hello.kt -include-runtime -d hello.jar
$ java -jar hello.jar

# Kotlin REPL
$ kotlinc
>>> println("Hello, Kotlin!")
Hello, Kotlin!

# Kotlin Script
$ kotlinc -script hello.kts
// build.gradle.kts(Gradle Kotlin DSL)
plugins {
    kotlin("jvm") version "2.0.0"
    application
}

group = "com.example"
version = "1.0.0"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
    testImplementation(kotlin("test"))
}

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

kotlin {
    jvmToolchain(21)
}

1.3 基本構文

// エントリーポイント
fun main() {
    println("Hello, Kotlin!")
}

// 引数付き
fun main(args: Array<String>) {
    println("Arguments: ${args.joinToString()}")
}

// 変数宣言
val immutable: String = "変更不可"    // val = 不変(推奨)
var mutable: String = "変更可能"      // var = 可変
val inferred = "型推論"               // 型推論 → String

// 基本型
val int: Int = 42
val long: Long = 42L
val double: Double = 3.14
val float: Float = 3.14f
val boolean: Boolean = true
val char: Char = 'K'
val string: String = "Kotlin"

// 文字列テンプレート
val name = "World"
println("Hello, $name!")                           // 変数参照
println("Length: ${name.length}")                    // 式の埋め込み
println("Name: ${if (name.isNotEmpty()) name else "Unknown"}")

// 複数行文字列
val json = """
    {
        "name": "$name",
        "version": ${1 + 1}
    }
""".trimIndent()

1.4 制御構造

// if式(式として値を返す)
val max = if (a > b) a else b

// when式(switchの強化版)
val result = when (status) {
    200 -> "OK"
    400 -> "Bad Request"
    404 -> "Not Found"
    in 500..599 -> "Server Error"
    else -> "Unknown"
}

// whenの高度な使用法
when {
    x.isNegative() -> println("Negative")
    x == 0 -> println("Zero")
    else -> println("Positive")
}

when (val response = executeRequest()) {
    is Success -> println(response.data)
    is Error -> println(response.message)
}

// forループ
for (i in 1..10) println(i)           // 1〜10(10を含む)
for (i in 1 until 10) println(i)      // 1〜9(10を含まない)
for (i in 10 downTo 1 step 2) println(i) // 10,8,6,4,2
for ((index, value) in list.withIndex()) println("$index: $value")

// whileループ
while (condition) { /* ... */ }
do { /* ... */ } while (condition)

1.5 関数

// 基本的な関数
fun add(a: Int, b: Int): Int {
    return a + b
}

// 単一式関数
fun add(a: Int, b: Int): Int = a + b
fun add(a: Int, b: Int) = a + b  // 戻り値型も推論

// デフォルト引数
fun greet(name: String, greeting: String = "Hello") = "$greeting, $name!"
greet("Kotlin")                    // "Hello, Kotlin!"
greet("Kotlin", "Hi")             // "Hi, Kotlin!"

// 名前付き引数
fun createUser(name: String, age: Int, email: String) { /* ... */ }
createUser(name = "Alice", age = 30, email = "alice@example.com")

// 可変長引数
fun sum(vararg numbers: Int): Int = numbers.sum()
sum(1, 2, 3, 4, 5)

// ローカル関数
fun processData(data: List<String>) {
    fun validate(item: String): Boolean = item.isNotBlank()
    data.filter(::validate).forEach(::println)
}

// 中置関数
infix fun Int.times(str: String) = str.repeat(this)
val result = 3 times "Hello "  // "Hello Hello Hello "

// 演算子オーバーロード
data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point) = Point(x + other.x, y + other.y)
}
val p = Point(1, 2) + Point(3, 4)  // Point(4, 6)

第2章: Null安全と型システム

2.1 Null安全

Kotlinの最も重要な機能の一つ。型システムでNullable型とNon-Null型を明確に区別する。

// Non-Null型(デフォルト)— nullを代入できない
var name: String = "Kotlin"
// name = null  // コンパイルエラー

// Nullable型(?付き)— nullを許容
var nullableName: String? = "Kotlin"
nullableName = null  // OK

// 安全呼び出し演算子(?.)
val length: Int? = nullableName?.length  // nullならnullを返す

// エルビス演算子(?:)
val length: Int = nullableName?.length ?: 0  // nullなら0
val name: String = nullableName ?: throw IllegalStateException("Name is null")

// 非null表明演算子(!!)— NullPointerExceptionの可能性
val length: Int = nullableName!!.length  // nullならNPE(使用は避ける)

// 安全キャスト
val str: String? = value as? String  // キャスト失敗ならnull

// let — nullチェックとスコープ関数
nullableName?.let { name ->
    println("Name: $name, Length: ${name.length}")
}

// Nullable型のコレクション操作
val list: List<String?> = listOf("a", null, "b", null, "c")
val nonNullList: List<String> = list.filterNotNull()  // ["a", "b", "c"]

2.2 型システム

// 型階層
//            Any  (すべての非Null型のスーパータイプ)
//           / | \
//         /   |   \
//       Int String  ... (各型)
//         \   |   /
//          \  |  /
//          Nothing  (すべての型のサブタイプ — 値を持たない)

// Any? はNullable型を含むすべての型のスーパータイプ
// Nothing? はnullのみを持つ

// スマートキャスト
fun processValue(value: Any) {
    if (value is String) {
        // このブロック内ではvalueはString型として扱える
        println(value.length)  // 明示的キャスト不要
    }
    when (value) {
        is Int -> println(value + 1)
        is String -> println(value.uppercase())
        is List<*> -> println(value.size)
    }
}

// ジェネリクス
class Box<T>(val value: T)
val intBox = Box(42)         // Box<Int>
val strBox = Box("Hello")   // Box<String>

// 共変・反変
class Producer<out T>(val value: T)      // out = 共変(Producer<Dog> → Producer<Animal>)
class Consumer<in T> { fun consume(item: T) {} }  // in = 反変

// 型射影(Use-site variance)
fun copy(from: Array<out Any>, to: Array<Any>) {
    for (i in from.indices) to[i] = from[i]
}

// スター射影
fun printAll(list: List<*>) {
    list.forEach { println(it) }
}

// Reified型パラメータ(inline関数でのみ使用可能)
inline fun <reified T> isType(value: Any): Boolean = value is T
println(isType<String>("hello"))  // true
println(isType<Int>("hello"))     // false

// 型エイリアス
typealias UserMap = Map<String, List<User>>
typealias Predicate<T> = (T) -> Boolean
typealias Handler = (Request, Response) -> Unit

第3章: オブジェクト指向プログラミング

3.1 クラスとプロパティ

// 基本クラス
class Person(val name: String, var age: Int)

// プライマリコンストラクタとinit
class Person(val name: String, var age: Int) {
    val isAdult: Boolean
    
    init {
        require(age >= 0) { "Age must be non-negative" }
        isAdult = age >= 18
    }
}

// セカンダリコンストラクタ
class Person(val name: String, var age: Int) {
    constructor(name: String) : this(name, 0)
}

// プロパティのカスタムアクセサ
class Circle(val radius: Double) {
    val area: Double
        get() = Math.PI * radius * radius
    
    var diameter: Double
        get() = radius * 2
        set(value) {
            // radiusはvalなのでsetは定義不可(この例ではdiameterのみ)
            println("Setting diameter to $value")
        }
}

// レイトイニシャライゼーション
class Service {
    lateinit var repository: Repository
    
    fun isInitialized(): Boolean = ::repository.isInitialized
}

// 遅延初期化(by lazy)
class Config {
    val databaseUrl: String by lazy {
        println("Loading config...")
        loadFromFile("config.properties")["db.url"]!!
    }
}

// バッキングフィールド
class Counter {
    var count: Int = 0
        private set  // setterをprivateに
    
    fun increment() { count++ }
}

3.2 継承とインターフェース

// open修飾子が必要(デフォルトはfinal)
open class Animal(val name: String) {
    open fun sound(): String = "..."
    fun describe(): String = "$name says ${sound()}"
}

class Dog(name: String) : Animal(name) {
    override fun sound(): String = "Woof!"
}

class Cat(name: String) : Animal(name) {
    override fun sound(): String = "Meow!"
}

// 抽象クラス
abstract class Shape {
    abstract fun area(): Double
    fun describe() = "Shape with area ${area()}"
}

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area() = width * height
}

// インターフェース
interface Drawable {
    fun draw()
    fun describe(): String = "Drawable object"  // デフォルト実装
}

interface Resizable {
    fun resize(factor: Double)
}

class Circle(var radius: Double) : Shape(), Drawable, Resizable {
    override fun area() = Math.PI * radius * radius
    override fun draw() = println("Drawing circle with radius $radius")
    override fun resize(factor: Double) { radius *= factor }
}

// sealed class(制限付き継承)
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String, val cause: Exception? = null) : Result<Nothing>()
    data object Loading : Result<Nothing>()
}

fun handleResult(result: Result<String>) = when (result) {
    is Result.Success -> println("Data: ${result.data}")
    is Result.Error -> println("Error: ${result.message}")
    is Result.Loading -> println("Loading...")
    // else不要(sealedですべてのケースを網羅)
}

// sealed interface(Kotlin 1.5+)
sealed interface Shape {
    data class Circle(val radius: Double) : Shape
    data class Rectangle(val width: Double, val height: Double) : Shape
    data class Triangle(val base: Double, val height: Double) : Shape
}

3.3 データクラスとその他の特殊クラス

// data class(equals, hashCode, toString, copy, componentNを自動生成)
data class User(
    val id: Long,
    val name: String,
    val email: String,
    val role: Role = Role.USER
)

val user = User(1, "Alice", "alice@example.com")
println(user)  // User(id=1, name=Alice, email=alice@example.com, role=USER)

val admin = user.copy(role = Role.ADMIN)  // 一部変更してコピー

val (id, name, email) = user  // 分割代入(destructuring)

// enum class
enum class Role(val level: Int) {
    USER(1), ADMIN(2), SUPER_ADMIN(3);
    
    fun hasPermission(requiredLevel: Int): Boolean = level >= requiredLevel
}

// value class(インライン化される軽量ラッパー)
@JvmInline
value class UserId(val value: Long)

@JvmInline
value class Email(val value: String) {
    init { require(value.contains("@")) { "Invalid email" } }
}

fun findUser(id: UserId): User? { /* ... */ }
findUser(UserId(42))  // 型安全、ランタイムオーバーヘッドなし

// object(シングルトン)
object Database {
    private val connection = createConnection()
    fun query(sql: String): ResultSet = connection.createStatement().executeQuery(sql)
}
Database.query("SELECT * FROM users")

// companion object(クラスのスタティックメンバー相当)
class User private constructor(val name: String) {
    companion object Factory {
        fun create(name: String): User = User(name.trim())
        fun fromJson(json: String): User { /* ... */ }
    }
}
val user = User.create("Alice")

第4章: 関数型プログラミング

4.1 高階関数とラムダ

// 高階関数(関数を引数に取る関数)
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
}

val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.customFilter { it % 2 == 0 }  // [2, 4]

// ラムダ式
val square: (Int) -> Int = { x -> x * x }
val double: (Int) -> Int = { it * 2 }  // 引数が1つならitで参照

// 関数参照
fun isEven(n: Int): Boolean = n % 2 == 0
val evens = numbers.filter(::isEven)

// メソッド参照
val lengths = listOf("a", "bb", "ccc").map(String::length)

// 関数を返す関数
fun multiplier(factor: Int): (Int) -> Int = { it * factor }
val triple = multiplier(3)
println(triple(5))  // 15

// クロージャ
fun counter(): () -> Int {
    var count = 0
    return { ++count }
}
val next = counter()
println(next())  // 1
println(next())  // 2

4.2 コレクション操作

val users = listOf(
    User(1, "Alice", 30), User(2, "Bob", 25),
    User(3, "Charlie", 35), User(4, "Diana", 28)
)

// フィルタリング
val adults = users.filter { it.age >= 30 }
val firstAdmin = users.firstOrNull { it.role == Role.ADMIN }

// マッピング
val names = users.map { it.name }
val nameWithAge = users.map { "${it.name} (${it.age})" }
val flatList = listOf(listOf(1, 2), listOf(3, 4)).flatMap { it }  // [1,2,3,4]

// ソート
val sorted = users.sortedBy { it.age }
val sortedDesc = users.sortedByDescending { it.name }

// グループ化
val byAge = users.groupBy { if (it.age >= 30) "senior" else "junior" }
val byRole = users.groupBy { it.role }

// 集約
val totalAge = users.sumOf { it.age }
val avgAge = users.map { it.age }.average()
val oldest = users.maxByOrNull { it.age }
val nameList = users.joinToString(", ") { it.name }

// fold / reduce
val sum = numbers.fold(0) { acc, n -> acc + n }
val product = numbers.reduce { acc, n -> acc * n }

// チェーン操作
val result = users
    .filter { it.age >= 25 }
    .sortedBy { it.name }
    .take(3)
    .map { "${it.name}: ${it.age}" }
    .joinToString("\n")

// シーケンス(遅延評価)
val result = users.asSequence()
    .filter { it.age >= 25 }
    .map { it.name.uppercase() }
    .take(2)
    .toList()

// マップ操作
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
val filtered = map.filter { (_, value) -> value > 1 }
val mapped = map.mapValues { (_, value) -> value * 10 }

4.3 スコープ関数

// let — nullチェック、変換、スコープ限定
val result = nullableValue?.let { value ->
    process(value)
}

// run — オブジェクトの初期化と計算
val result = service.run {
    port = 8080
    host = "localhost"
    start()
    "Service started on $host:$port"
}

// with — オブジェクトのプロパティアクセス
val info = with(user) {
    "Name: $name, Age: $age, Email: $email"
}

// apply — オブジェクトの設定(オブジェクト自身を返す)
val user = User().apply {
    name = "Alice"
    age = 30
    email = "alice@example.com"
}

// also — 副作用(デバッグ、ロギング)
val numbers = mutableListOf(1, 2, 3)
    .also { println("Before: $it") }
    .apply { add(4) }
    .also { println("After: $it") }

// 使い分けガイド:
// let   → Nullable処理、変換                   it参照、ラムダの結果を返す
// run   → オブジェクト初期化+計算              this参照、ラムダの結果を返す
// with  → 既存オブジェクトのプロパティ一括アクセス this参照、ラムダの結果を返す
// apply → オブジェクト設定                     this参照、オブジェクト自身を返す
// also  → 副作用(ログ等)                     it参照、オブジェクト自身を返す

第5章: コルーチン — Kotlinの非同期プログラミング

5.1 コルーチンの基本

import kotlinx.coroutines.*

// 基本的なコルーチン起動
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}
// 出力: Hello, (1秒後) World!

// async/await パターン
fun main() = runBlocking {
    val deferred1 = async { fetchUserFromApi(1) }
    val deferred2 = async { fetchUserFromApi(2) }
    
    val user1 = deferred1.await()
    val user2 = deferred2.await()
    println("Users: $user1, $user2")
}

// 構造化された並行性
fun main() = runBlocking {
    coroutineScope {
        launch { task1() }
        launch { task2() }
    }
    println("Both tasks completed")
}

5.2 コルーチンビルダー

// launch — Fire-and-forget(Jobを返す)
val job: Job = scope.launch {
    // 非同期処理
}
job.join()   // 完了を待つ
job.cancel() // キャンセル

// async — 結果を返す非同期処理(Deferredを返す)
val deferred: Deferred<User> = scope.async {
    fetchUser(userId)
}
val user: User = deferred.await()

// withContext — コンテキスト切り替え
suspend fun fetchData(): Data = withContext(Dispatchers.IO) {
    // IOスレッドで実行
    api.getData()
}

// coroutineScope — 構造化された並行性
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val user = async { fetchUser() }
    val orders = async { fetchOrders() }
    val recommendations = async { fetchRecommendations() }
    
    Dashboard(user.await(), orders.await(), recommendations.await())
}

5.3 ディスパッチャー

// Dispatchers.Main      — メインスレッド(UI更新用)
// Dispatchers.IO        — I/O操作(ネットワーク、ディスク)
// Dispatchers.Default   — CPU集約的な処理
// Dispatchers.Unconfined — 制限なし(テスト用)

launch(Dispatchers.IO) {
    val data = fetchFromNetwork()   // IOスレッドで実行
    withContext(Dispatchers.Main) {
        updateUI(data)              // メインスレッドで実行
    }
}

// カスタムディスパッチャー
val customDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()

5.4 Flow — リアクティブストリーム

import kotlinx.coroutines.flow.*

// 基本的なFlow
fun numberFlow(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}

fun main() = runBlocking {
    numberFlow()
        .filter { it % 2 == 0 }
        .map { it * it }
        .collect { println(it) }
    // 出力: 4, 16
}

// Flow演算子
val result = flow
    .filter { it.isValid() }
    .map { transform(it) }
    .onEach { log(it) }
    .catch { e -> emit(defaultValue) }
    .flowOn(Dispatchers.IO)
    .stateIn(scope, SharingStarted.WhileSubscribed(5000), initialValue)

// StateFlow(状態の保持)
class ViewModel {
    private val _uiState = MutableStateFlow(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val data = repository.fetchData()
                _uiState.value = UiState.Success(data)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message)
            }
        }
    }
}

// SharedFlow(イベントのブロードキャスト)
class EventBus {
    private val _events = MutableSharedFlow<Event>(replay = 0)
    val events: SharedFlow<Event> = _events.asSharedFlow()
    
    suspend fun emit(event: Event) = _events.emit(event)
}

// channelFlow — 並行的なFlow生成
fun mergedFlow(): Flow<Data> = channelFlow {
    launch { source1.collect { send(it) } }
    launch { source2.collect { send(it) } }
}

5.5 エラーハンドリングとキャンセル

// 例外処理
val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught: ${exception.message}")
}

val job = scope.launch(handler) {
    throw RuntimeException("Something went wrong")
}

// supervisorScope — 子コルーチンの失敗を独立化
supervisorScope {
    val job1 = launch { task1() }  // 失敗してもjob2に影響しない
    val job2 = launch { task2() }
}

// キャンセル
val job = launch {
    repeat(1000) { i ->
        ensureActive()  // キャンセルチェック(またはyield())
        println("Processing $i")
        delay(100)       // delay()はキャンセルに対応
    }
}
delay(500)
job.cancelAndJoin()

// withTimeout
val result = withTimeoutOrNull(3000L) {
    fetchData()
} ?: defaultValue

第6章: 拡張関数とDSL

6.1 拡張関数

// String拡張
fun String.toSlug(): String = this
    .lowercase()
    .replace(Regex("[^a-z0-9\\s-]"), "")
    .replace(Regex("\\s+"), "-")
    .trim('-')

"Hello World!".toSlug()  // "hello-world"

// コレクション拡張
fun <T> List<T>.second(): T = this[1]
fun <T> List<T>.secondOrNull(): T? = this.getOrNull(1)

// 拡張プロパティ
val String.wordCount: Int
    get() = this.split(Regex("\\s+")).size

"Hello World Kotlin".wordCount  // 3

// Nullable型の拡張
fun String?.orEmpty(): String = this ?: ""
fun <T> T?.orDefault(default: T): T = this ?: default

6.2 DSL構築

// HTML DSL
class HTML {
    private val elements = mutableListOf<String>()
    
    fun head(init: Head.() -> Unit) { /* ... */ }
    fun body(init: Body.() -> Unit) { /* ... */ }
    override fun toString() = elements.joinToString("\n")
}

fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)

val page = html {
    head { title("My Page") }
    body {
        h1("Welcome")
        p("This is Kotlin DSL")
        ul {
            li("Item 1")
            li("Item 2")
        }
    }
}

// Ktor ルーティングDSL(実際のフレームワーク例)
fun Application.configureRouting() {
    routing {
        get("/") { call.respondText("Hello, World!") }
        
        route("/api/v1") {
            get("/users") { /* ... */ }
            post("/users") { /* ... */ }
            
            route("/users/{id}") {
                get { /* ... */ }
                put { /* ... */ }
                delete { /* ... */ }
            }
        }
    }
}

// Gradle Kotlin DSL
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
    implementation("io.ktor:ktor-server-core:2.3.0")
    testImplementation(kotlin("test"))
}

// テストDSL
class UserTest {
    @Test
    fun `should create user with valid data`() {
        val user = user {
            name = "Alice"
            age = 30
            email = "alice@example.com"
        }
        
        user shouldBe User("Alice", 30, "alice@example.com")
        user.name shouldStartWith "A"
        user.age shouldBeGreaterThan 18
    }
}

第7章: サーバーサイドKotlin

7.1 Ktor(JetBrains製Webフレームワーク)

// Application.kt
fun main() {
    embeddedServer(Netty, port = 8080) {
        configureSerialization()
        configureRouting()
        configureAuthentication()
    }.start(wait = true)
}

fun Application.configureSerialization() {
    install(ContentNegotiation) {
        json(Json { prettyPrint = true; isLenient = true })
    }
}

fun Application.configureRouting() {
    routing {
        route("/api/users") {
            get {
                val users = userService.findAll()
                call.respond(users)
            }
            get("/{id}") {
                val id = call.parameters["id"]?.toLongOrNull()
                    ?: return@get call.respond(HttpStatusCode.BadRequest, "Invalid ID")
                val user = userService.findById(id)
                    ?: return@get call.respond(HttpStatusCode.NotFound)
                call.respond(user)
            }
            post {
                val request = call.receive<CreateUserRequest>()
                val user = userService.create(request)
                call.respond(HttpStatusCode.Created, user)
            }
        }
    }
}

7.2 Spring Boot with Kotlin

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

@RestController
@RequestMapping("/api/users")
class UserController(private val userService: UserService) {
    
    @GetMapping
    suspend fun findAll(): List<User> = userService.findAll()
    
    @GetMapping("/{id}")
    suspend fun findById(@PathVariable id: Long): ResponseEntity<User> =
        userService.findById(id)
            ?.let { ResponseEntity.ok(it) }
            ?: ResponseEntity.notFound().build()
    
    @PostMapping
    suspend fun create(@RequestBody @Valid request: CreateUserRequest): ResponseEntity<User> {
        val user = userService.create(request)
        return ResponseEntity.status(HttpStatus.CREATED).body(user)
    }
}

@Service
class UserService(private val userRepository: UserRepository) {
    suspend fun findAll(): List<User> = userRepository.findAll().toList()
    suspend fun findById(id: Long): User? = userRepository.findById(id)
    suspend fun create(request: CreateUserRequest): User =
        userRepository.save(request.toEntity())
}

第8章: Kotlin Multiplatformとまとめ

8.1 Kotlin Multiplatform(KMP)

// 共通コード(commonMain)
expect fun platformName(): String

class Greeting {
    fun greet(): String = "Hello from ${platformName()}!"
}

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

// iOS実装(iosMain)
actual fun platformName(): String = UIDevice.currentDevice.systemName()

// JVM実装(jvmMain)
actual fun platformName(): String = "JVM ${System.getProperty("java.version")}"
// build.gradle.kts(KMPプロジェクト)
kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    jvm()
    
    sourceSets {
        commonMain.dependencies {
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
            implementation("io.ktor:ktor-client-core:2.3.0")
            implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
        }
        androidMain.dependencies {
            implementation("io.ktor:ktor-client-okhttp:2.3.0")
        }
        iosMain.dependencies {
            implementation("io.ktor:ktor-client-darwin:2.3.0")
        }
    }
}

まとめ

Kotlinは、モダンなプログラミング言語として以下の強力な機能を提供する:

  1. Null安全: 型システムでNPEを防止、?., ?:, let
  2. 簡潔な構文: データクラス、型推論、文字列テンプレート、デフォルト引数
  3. OOP: sealed class、value class、companion object、委譲パターン
  4. 関数型: 高階関数、ラムダ、コレクション操作、シーケンス
  5. コルーチン: 構造化された並行性、Flow、StateFlow/SharedFlow
  6. DSL構築: 拡張関数、レシーバー付きラムダ、型安全なビルダー
  7. サーバーサイド: Ktor、Spring Boot、Exposed
  8. マルチプラットフォーム: KMP、Compose Multiplatform

参考文献