Kotlin: Kotlinの制御フロー
最終更新:2026-08-26
Kotlinの制御フローは単なるフロー制御にとどまりません。if や when は戻り値を持つ式であり、これによりCharlieの注文ルーティングロジックは安全かつ簡潔なものになっています。
1. 学習内容
ifは式です:val max = if (a > b) a else bwhenのあらゆる形態:多値、ガード条件、変数束縛for+ イテレータ:コレクション、範囲、マップを反復処理する- ラベルと返品:
break@label/continue@label/return@forEach - Charlieの活躍:
when(order.status)注文ルーティング
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) 注文状況のルーティング図
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 -> — まず型を一致させ、その後に条件を追加します。📖 まとめ
ifは、Java の三項演算子の代わりとして、変数に直接代入できる式ですwhenはswitchのアップグレード版です:複数値のマッチング、範囲、型、ガード条件when式として使用される場合、コンパイラは分岐の漏れを防ぐために網羅性を強制しますforは、範囲、コレクション、マップ、およびカスタムイテレータをサポートしていますbreak@およびcontinue@を含むラベルにより、ネストされたループの移動を正確に制御します- ラムダのローカルリターン
return@forEach対 非ローカルリターンreturn
📝 練習問題
- 初心者 (⭐):
if式を使用して、3つの数値のうち最大値を返すmaxOfThree関数を実装してください。ヒント:if文をネストするか、標準ライブラリのmaxOf()を使用してください。 - 中級 (⭐⭐):
whenを使用して注文の優先順位を決定します:VIP(10,000 USD 以上)> 高優先度(1,000 以上)> 通常、キャンセルされた注文はスキップします。 ヒント:whenのガード条件 - 課題 (⭐⭐⭐): ループラベルを使用して、注文のバッチ処理を実装してください。外側のループは顧客バッチを、内側のループは注文を処理します。キャンセルされた注文が見つかった場合は、その顧客バッチ全体をスキップしてください。ヒント:
loop@+continue@loop