1. 背景:インライン関数と非ローカルリターン
Kotlin の inline 関数は 高階関数のオーバーヘッドを削減 できるだけでなく、
ラムダ内で 非ローカルリターン(外側の関数から return)を可能にします。
例:非ローカルリターン
inline fun forEach(list: List<Int>, action: (Int) -> Unit) {
for (item in list) action(item)
}
fun main() {
val numbers = listOf(1, 2, 3, 4)
forEach(numbers) {
if (it == 3) return // ✅ main() からリターン
println(it)
}
println("End") // 実行されない
}
便利ですが、「意図しない return」が外側に伝播してしまう危険があります。
2. crossinline とは?
crossinline を付けると、
- ラムダはインライン化される(オーバーヘッドなし)
-
非ローカルリターンは禁止(ラムダ内では
returnできない)
という制約が課されます。
3. crossinline の例
inline fun runTask(crossinline action: () -> Unit) {
val runnable = Runnable {
action() // インライン化されるが非ローカルリターンは不可
}
runnable.run()
}
fun main() {
runTask {
println("Start")
// return // ❌ コンパイルエラー(非ローカルリターン禁止)
println("End")
}
}
Runnable やコールバックに渡すケースでは、非ローカルリターンがあると設計上危険。
そこで crossinline を付けて安全にする。
4. crossinline を使う場面
-
コールバックや非同期処理
- ラムダを別スレッドで実行する場合、外側関数への
returnは不自然なので禁止する。 - 例:
Runnable,Thread,Async APIなどに渡すラムダ
- ラムダを別スレッドで実行する場合、外側関数への
-
関数オブジェクトに変換して保持する場合
- ラムダをクロージャに包んで保持する時、非ローカルリターンがあると制御フローが破壊されるため禁止。
-
安全な設計を強制したい場合
-
inlineを活かしてオーバーヘッドを減らしつつ、return による外側終了は避けたいときに使う。
-
5. まとめ表
| 修飾子 | インライン化 | 非ローカルリターン | 関数オブジェクト化 |
|---|---|---|---|
inline |
✅ される | ✅ 許可 | ❌ 不可 |
noinline |
❌ されない | ❌ 禁止 | ✅ 可 |
crossinline |
✅ される | ❌ 禁止 | ❌ 不可 |
まとめ
-
inlineのラムダは非ローカルリターン可能だが、予期せぬ制御フローを生む危険がある -
crossinlineを付けると インライン化はされるが、非ローカルリターンは禁止 - 主な使いどころは コールバック・非同期処理・Runnable などに渡すラムダ
-
inline/noinline/crossinlineを組み合わせることで柔軟かつ安全に設計できる