単一式関数 / ローカル関数
| 対応: | Kotlin 1.0(2016) |
|---|
Kotlinでは関数が1つの式で完結する場合、中括弧と『return』を省略した「単一式関数」として書けます。また関数の中に関数を定義する「ローカル関数」で処理をスコープ内にカプセル化できます。
構文
// 通常の関数
fun double(x: Int): Int {
return x * 2
}
// 単一式関数(= を使って簡潔に書けます)
fun double(x: Int): Int = x * 2
// 型推論で戻り値型も省略できます
fun double(x: Int) = x * 2
// ローカル関数(関数の中に関数を定義します)
fun process(numbers: List<Int>): List<Int> {
fun validate(n: Int): Boolean = n > 0 // ローカル関数
return numbers.filter { validate(it) }
}
構文一覧
| 構文 | 概要 |
|---|---|
| fun name(p) = 式 | 単一式関数です。= の右辺がそのまま戻り値になります。 |
| 戻り値型の省略 | 式から型が推論できる場合は戻り値型を省略できます。 |
| ローカル関数 | 関数の内部で定義する関数です。外側のスコープの変数にアクセスできます。 |
| ネスト関数 | ローカル関数内でさらにローカル関数を定義できます。 |
サンプルコード
sample_single_expr_local_function.kt
// 単一式関数の例
fun square(n: Int) = n * n
fun greet(name: String) = "Hello, $name!"
fun isEven(n: Int) = n % 2 == 0
fun max(a: Int, b: Int) = if (a > b) a else b
// ローカル関数の例
fun findDuplicates(numbers: List<Int>): List<Int> {
// ローカル関数 — 外側の変数 numbers にアクセスできます
fun hasDuplicate(n: Int): Boolean =
numbers.count { it == n } > 1
return numbers.filter { hasDuplicate(it) }.distinct()
}
// ローカル関数で再帰を使う例
fun factorial(n: Int): Long {
fun calc(x: Int, acc: Long): Long = // 末尾再帰用ローカル関数
if (x <= 1) acc else calc(x - 1, acc * x)
return calc(n, 1L)
}
fun main() {
// 単一式関数の呼び出し
println(square(5)) // 25
println(greet("Kotlin")) // Hello, Kotlin!
println(isEven(4)) // true
println(max(10, 7)) // 10
// ローカル関数の呼び出し
val nums = listOf(1, 2, 3, 2, 4, 3, 5)
println(findDuplicates(nums)) // [2, 3]
// 階乗(ローカル関数による末尾再帰)
println(factorial(5)) // 120
println(factorial(10)) // 3628800
// 単一式関数を組み合わせる
fun cube(n: Int) = n * square(n) // square は上で定義した関数
println(cube(3)) // 27
}
single_expr_local_function.kt
kotlinc single_expr_local_function.kt -include-runtime -d single_expr_local_function.jar java -jar single_expr_local_function.jar 25 Hello, Kotlin! true 10 [2, 3] 120 3628800 27
よくあるミス
sample_single_expr_local_function_mistakes.kt
// 複数文になる処理を単一式関数で書こうとする
// 単一式関数は式(expression)1つだけ書ける — 文(statement)は書けない
// fun setup(name: String) = { // ラムダを返す関数になってしまう
// println("名前: $name") // これはラムダの中身
// name.uppercase() // これも実行されない
// }
// 複数の処理が必要なときは通常の中括弧スタイルを使う
// ローカル関数を定義前に呼ぶ
fun computeScore(values: List<Int>): Int {
// validate(0) // コンパイルエラー: ローカル関数は定義後しか使えない
fun validate(n: Int) = n >= 0 // 定義はここ
return values.filter { validate(it) }.sum()
}
// 単一式関数の戻り値が Unit になることを見落とす
// 式が Unit を返す場合は Unit が推論される
// 別の型を返すつもりで書いた場合、意図が伝わらずバグになる
fun printGreeting(name: String) = println("こんにちは、$name!")
// 戻り値は Unit(println が Unit を返すため)— 値を使おうとすると Unit になる
fun main() {
println(computeScore(listOf(-1, 5, 3, -2, 8)))
printGreeting("牧瀬紅莉栖")
val ret = printGreeting("岡部倫太郎")
println("戻り値: $ret") // 戻り値: kotlin.Unit
}
コンパイルして実行すると次のようになります。
kotlinc sample_single_expr_local_function_mistakes.kt -include-runtime -d sample_single_expr_local_function_mistakes.jar java -jar sample_single_expr_local_function_mistakes.jar 16 こんにちは、牧瀬紅莉栖! こんにちは、岡部倫太郎! 戻り値: kotlin.Unit
概要
単一式関数は『=』を使って1行で定義できるため、シンプルな変換関数やユーティリティ関数を簡潔に書けます。ただし処理が複雑になる場合は通常の中括弧スタイルのほうが読みやすいです。
ローカル関数は外側の関数の変数にアクセスできるクロージャです。外部から呼ばれない補助ロジックをローカル関数にまとめることで、スコープを外側の関数の内部に限定できます。同様の目的でラムダ式も使えますが、ローカル関数は再帰が書きやすいという利点があります。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。