Kotlin: Kotlinの制御フロー

最終更新:2026-08-26

Kotlinの制御フローは単なるフロー制御にとどまりません。ifwhen は戻り値を持つ式であり、これによりCharlieの注文ルーティングロジックは安全かつ簡潔なものになっています。

1. 学習内容


2. ある建築家の実話

(1) 課題:ネストされたif-elseの地獄

Charlieが開発した「Java OrderProcessor」では、注文のステータスに応じて処理を振り分けるために、5段階のネストされたif-else文が使用されていました。どのパイプラインを使用するかを決定するだけで、120行ものコードが必要だったのです。新しいチームメンバーのBobが加わった際、彼は分岐条件を3回も間違えてしまい、本番環境でのインシデントを引き起こしてしまいました。

(2) Kotlinのwhen式による解決策

KOTLIN
// Clean, exhaustive, compiler-verified
fun route(order: Order): Pipeline = when (order.status) {
    Status.PENDING -> PendingPipeline
    Status.CONFIRMED -> if (order.total > 10_000) VipPipeline else StandardPipeline
    Status.SHIPPED -> TrackingPipeline
    Status.CANCELLED -> RefundPipeline
}

when式の網羅性チェックとコンパイラの検証により、分岐が欠けている場合はコンパイルエラーになります。これでBobが同じバグを起こすことはありません。


3. if 式

(1) if が戻り値を持つ式である場合

KOTLIN
// if as expression
val max = if (a > b) a else b

// Multi-branch if expression
val category = if (total > 10_000) {
    println("High value order")
    "VIP"
} else if (total > 1_000) {
    "PRIORITY"
} else {
    "STANDARD"
}

(2) if Expression 対 Java の if

次元 Javaの場合 Kotlinの場合
自然 声明 表現
戻り値 None 最後の式の値
課題 追加の変数が必要 val x = if(...)...
三項演算子 ?: if/else
KOTLIN
// No ternary operator needed - if/else IS the ternary
val discount = if (total > 5_000) 0.15 else if (total > 1_000) 0.10 else 0.0

4. あらゆる形態において

(1) 基本の「when」式

KOTLIN
// Exact match
val label = when (status) {
    "PENDING" -> "Waiting"
    "CONFIRMED" -> "Confirmed"
    else -> "Unknown"
}

// Multiple values in one branch
val isTerminal = when (status) {
    "SHIPPED", "DELIVERED", "CANCELLED" -> true
    else -> false
}

(2) ガード条件(ガード状態の場合)

KOTLIN
// Conditional branches with 'in' and ranges
val pipeline = when {
    status == "CANCELLED" -> "REFUND"
    total > 10_000 && priority == "URGENT" -> "VIP_EXPRESS"
    total > 10_000 -> "VIP"
    total > 1_000 -> "PRIORITY"
    status in listOf("NEW", "PENDING") -> "QUEUE"
    else -> "STANDARD"
}

(3) 変数の束縛と構造分解

KOTLIN
// Capture with when subject
val result = when (val response = httpClient.call()) {
    is Success -> "OK: ${response.data}"
    is Error -> "Failed: ${response.code}"
}
// response is smart-cast and available in branches

(4) 注文状況のルーティング図

100%
flowchart TD
    A[when order.status] --> B{PENDING}
    A --> C{CONFIRMED}
    A --> D{SHIPPED}
    A --> E{CANCELLED}
    B --> B1[Waiting Queue]
    C --> C1{total > 10000?}
    C1 -->|Yes| C2[VIP Pipeline]
    C1 -->|No| C3[Standard Pipeline]
    D --> D1[Tracking Pipeline]
    E --> E1[Refund Queue]

(5) フォームの比較時

フォーム 構文 使用例
完全一致 when(status) 列挙型 / 文字列 / 定数
複数値の一致 "A", "B" -> ... 同一ブランチ内の複数値
範囲一致 in 1..10 数値範囲
タイプマッチ is String タイプベースのルーティング
ガード条件 when { ... } 複雑な条件の組み合わせ
変数の束縛 when(val x = ...) 計算された値の取得

5. forループとイテレータ

(1) 基本的なforループ

KOTLIN
// Range iteration
for (i in 1..10) { /* i from 1 to 10 */ }

// List iteration
for (order in orders) { println(order.id) }

// With index
for ((index, order) in orders.withIndex()) {
    println("[$index] ${order.id}")
}

// Map iteration
for ((id, status) in orderStatusMap) {
    println("$id: $status")
}

(2) イテレータプロトコル

KOTLIN
// Any class with operator fun iterator() can be used in for-loop
class OrderBatch(val orders: List<Order>) {
    operator fun iterator(): Iterator<Order> = orders.iterator()
}

val batch = OrderBatch(orders)
for (order in batch) { /* works! */ }

(3) forループのパターンの比較

パターン 構文 ユースケース
範囲 for (i in 1..10) ループ回数
コレクション for (o in orders) 要素の反復処理
インデックスあり for ((i, o) in ...) インデックスが必要
マップ for ((k,v) in map) キー・バリュー反復法
カスタムイテレータ operator fun iterator() カスタムコンテナ

6. whileループ

KOTLIN
// Standard while
var retryCount = 0
while (retryCount < 3) {
    val success = tryProcessOrder()
    if (success) break
    retryCount++
}

// do-while (executes at least once)
var input: String
do {
    input = readOrderStatus()
} while (input !in listOf("CONFIRM", "CANCEL"))

7. ラベルとリターン

(1) ループラベル

KOTLIN
// Label outer loop
loop@ for (batch in batches) {
    for (order in batch) {
        if (order.status == "CANCELLED") continue@loop  // Skip to next batch
        if (order.total > 100_000) break@loop  // Stop all processing
        process(order)
    }
}

(2) ラムダの戻り値

KOTLIN
// return from lambda (local return)
orders.forEach { order ->
    if (order.status == "SKIP") return@forEach  // Skip this item only
    process(order)
}

// return from enclosing function (non-local return)
// Only works in inline functions
orders.forEach { order ->
    if (order.status == "FATAL") return  // Exits main()
    process(order)
}

(3) ラベルの種類比較

ラベルの種類 構文 効果
ループラベル loop@ + break@loop 指定されたループを終了
ラベルを続行 continue@loop 指定されたループの次の反復へジャンプ
ラムダのローカルリターン return@forEach 現在のラムダのみを終了
非局所リターン return 囲んでいる関数から抜け出す(インライン関数のみ)

8. 完全な例:OrderProcessor の注文ステートマシン

▶ サンプル:注文ステートマシン

KOTLIN
// ============================================
// OrderProcessor - Status-Based Routing
// Feature: Full order status routing with when
// ============================================

enum class Status { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }
data class Order(val id: String, val total: Double, val status: Status, val region: String)

fun routeOrder(order: Order): String = when (order.status) {
    Status.PENDING -> when {
        order.region == "EU" -> "EU_COMPLIANCE_QUEUE"
        order.total > 10_000 -> "HIGH_VALUE_QUEUE"
        else -> "STANDARD_QUEUE"
    }
    Status.CONFIRMED -> when {
        order.total > 5_000 -> "EXPRESS_SHIPPING"
        else -> "REGULAR_SHIPPING"
    }
    Status.SHIPPED -> "TRACKING_UPDATE"
    Status.DELIVERED -> "COMPLETION_QUEUE"
    Status.CANCELLED -> when {
        order.total > 1_000 -> "PRIORITY_REFUND"
        else -> "STANDARD_REFUND"
    }
}

fun processBatch(orders: List<Order>) {
    loop@ for (order in orders) {
        when (order.status) {
            Status.CANCELLED -> {
                println("SKIP: ${order.id} cancelled")
                continue@loop
            }
            else -> {
                val pipeline = routeOrder(order)
                val priority = if (order.total > 10_000) "CRITICAL" else "NORMAL"
                println("PROCESS: ${order.id} -> $pipeline [$priority]")
            }
        }
    }
}

fun main() {
    val orders = listOf(
        Order("ORD-001", 299.99, Status.CONFIRMED, "US"),
        Order("ORD-002", 15_000.00, Status.PENDING, "EU"),
        Order("ORD-003", 2_500.00, Status.CONFIRMED, "US"),
        Order("ORD-004", 45.50, Status.CANCELLED, "US"),
        Order("ORD-005", 800.00, Status.PENDING, "ASIA"),
        Order("ORD-006", 8_000.00, Status.SHIPPED, "US")
    )

    println("=== Order Routing ===")
    processBatch(orders)

    // Summary using when expression results
    val summary = orders
        .filter { it.status != Status.CANCELLED }
        .groupBy { routeOrder(it) }
        .mapValues { (pipeline, list) -> "${list.size} orders" }

    println("\n=== Pipeline Summary ===")
    summary.forEach { (pipeline, info) -> println("  $pipeline: $info") }
}

出力:

TEXT 📖 参照専用
=== Order Routing ===
PROCESS: ORD-001 -> REGULAR_SHIPPING [NORMAL]
PROCESS: ORD-002 -> EU_COMPLIANCE_QUEUE [CRITICAL]
PROCESS: ORD-003 -> EXPRESS_SHIPPING [NORMAL]
SKIP: ORD-004 cancelled
PROCESS: ORD-005 -> STANDARD_QUEUE [NORMAL]
PROCESS: ORD-006 -> TRACKING_UPDATE [NORMAL]

=== Pipeline Summary ===
  REGULAR_SHIPPING: 1 orders
  EU_COMPLIANCE_QUEUE: 1 orders
  EXPRESS_SHIPPING: 1 orders
  STANDARD_QUEUE: 1 orders
  TRACKING_UPDATE: 1 orders

❓ よくある質問

Q Kotlinには三項演算子 ?: がありますか?
A いいえ。Kotlinでは、三項演算子の代わりに if/else を使用します。これは、if が式であり、機能的には同等でありながら、より読みやすいからです。
Q 主語がある場合とない場合での when の違いは何ですか?
A when(x) は、コンパイラの網羅性チェックを用いて x の値/型と照合します。 when { }は任意の条件式であり、elseを含まなければなりません。
Q forループでカスタムオブジェクトを反復処理することはできますか?
A はい、そのクラスがoperator fun iterator()を実装し、Iteratorを返す限り可能です。
Q 非局所リターンの制限事項は何ですか?
A 非局所リターンは、inline 関数のラムダ式でのみ使用できます。通常のラムダ式で非局所リターンを使用すると、コンパイルエラーが発生します。
Q when では分岐順序は重要ですか?
A はい。when は上から下へと照合を行い、最初に条件を満たした分岐が優先されます。より具体的な条件を、より一般的な条件よりも前に配置してください。
Q when で値と条件の両方を一致させるにはどうすればよいですか?
A ガード条件を使用します:is Order && total > 1000 -> — まず型を一致させ、その後に条件を追加します。

📖 まとめ


📝 練習問題

  1. 初心者 (⭐): if 式を使用して、3つの数値のうち最大値を返す maxOfThree 関数を実装してください。ヒント:if文をネストするか、標準ライブラリの maxOf() を使用してください。
  2. 中級 (⭐⭐): when を使用して注文の優先順位を決定します:VIP(10,000 USD 以上)> 高優先度(1,000 以上)> 通常、キャンセルされた注文はスキップします。 ヒント:when のガード条件
  3. 課題 (⭐⭐⭐): ループラベルを使用して、注文のバッチ処理を実装してください。外側のループは顧客バッチを、内側のループは注文を処理します。キャンセルされた注文が見つかった場合は、その顧客バッチ全体をスキップしてください。ヒント: loop@ + continue@loop

← 前へ | 次へ →

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%