公開日:2020/01/29更新日:2020/08/03
【Kotlin基礎】スコープ関数runの実装方法と用途を解説

- runの実装方法が知りたい。
- runはどんな時に使うと便利なの?
- run以外のスコープ関数について知りたい。
プログラミングを始めたばかりの方や、これまでJavaしか触ってこなかったにとって、「スコープ関数」という用語は初めて聞くのではないでしょうか?
Kotlinには、スコープ関数という便利な関数があります。
スコープ関数にはいくつか種類があるのですが、本記事では、runというスコープ関数について解説致します。
他のスコープ関数につきましても、簡単な解説と、詳細をまとめた記事へのリンクがありますので、そちらも合わせてご覧頂けたらと思います。
目次
スコープ関数runの実装方法と用途について
runの基本的な使い方
スコープ関数runは、公式ドキュメントで以下のように定義されております。
- Kotlin
inline fun run(block: () -> R): R
inline fun T.run(block: T.() -> R): R
【公式ドキュメント】 : run – Kotlin Programming Language
説明文には、「Calls the specified function block and returns its result.(指定された機能ブロックを呼び出し、その結果を返します。)」と記されています。
いまいちピンときませんね。僕はrunを「ブロック{ }内の処理を実行し、最後の処理を返す」関数と解釈しております。
実際の使用例で、詳しく見ていきましょう。
- Kotlin
val number = 10
val result = number.run {
1 * 1
3 * 3
5 * 5
}
println(result)
さて、変数resultをprintln()すると、なんと出力されるでしょうか?
正解は25です。runのブロック{ }の処理が行われ、最後の処理である5 * 5の結果がresultに格納されます。
上記の例では、変数numberからrunが呼び出されています。このようなオブジェクトをレシーバーと呼びます。
レシーバーは、runのブロック内で、thisで参照することができます。では、thisを使った例をみていきましょう。
- Kotlin
val number = 10
val result = number.run {
this + 10
}
println(result)
// 20
thisは変数numberの10なので、10 + 10で20が出力されます。
また、runの戻り値は、レシーバーの型に合わせる必要はありません。
- Kotlin
val number = 10
val hello = "こんにちは"
val result = number.run {
this + 10
hello
}
println(result)
// こんにちは
以上がrunの基本的な使い方となります。
- runは、ブロック{ }内の処理を実行する
- ブロック内の最後の処理を結果として返す
- runを呼び出すオブジェクトをレシーバーと呼ぶ
- ブロック内では、レシーバーをthisで呼び出せる
runの具体的な使用用途
続いて、runの具体的な使用用途について解説していきます。
といいましても、「これがrunの正しい使い方!」というものではなく、あくまで僕自身が便利だと感じる使用方法についての紹介となること、あらかじめご了承ください。
僕はrunをレシーバーのnullチェックに使用しております。
まずは、以下の2つのサンプルコードをご覧ください。
- サンプル1
- サンプル2
val str: String? = null
str.run {
println("1")
}
str?.run {
println("2")
}
str?:run {
println("3")
}
// 1、3
val str: String? = "Nullじゃない"
str.run {
println("1")
}
str?.run {
println("2")
}
str?:run {
println("3")
}
// 1、2
2つのサンプルコードの違いは、レシーバーとなっている変数strが、nullかどうかの違いです。
1つ目のサンプルコードでは、1と3が出力され、2つ目のサンプルコードでは1と2が出力されております。
出力結果に違いがあるのは、レシーバーの後の ?. や ?: でnullチェックを行っているからです。
サンプルコードからもわかるとおり、
?. をつけた場合は、「レシーバーがnullでなければブロック処理を実効」
?: をつけた場合は、「レシーバーがnullならブロック処理を実行」
となります。
Javaではnullチェックはif文で行っていると思います。
Javaで実装した場合と見比べると、runの便利さに気づいていただけるかもしれません。
- Kotlin
- Java
val str = null
str?.run {
// 処理
}
String str = null;
if(str != null) {
// 処理
}
- runはレシーバーのnullチェックに便利
- ?. をつけた場合は、nullでなければ実効
- ?: をつけた場合は、nullであれば実効
run以外のスコープ関数について
run以外のスコープ関数について簡単な解説と、詳しい内容をまとめた記事へのリンクを紹介致します。
letについて
letは、公式ドキュメントで以下のように定義されております。
- Kotlin
inline fun T.let(block: (T) -> R): R
【公式ドキュメント】 : let – Kotlin Programming Language
【使用例】
- Kotlin
val str = "hello"
val result = str.let {
it.length
}
println(result)
// 5
- ブロック処理内では、レシーバーをitで参照できる
- ブロック処理の最後の値を返す(レシーバーでなくてもよい)
詳しい解説はこちらをご覧ください。
applyについて
applyは、公式ドキュメントで以下のように定義されております。
- Kotlin
inline fun T.apply(block: T.() -> Unit): T
【公式ドキュメント】 : apply – Kotlin Programming Language
【使用例】
- Kotlin
class User {
var name: String = ""
var age: Int = 0
}
val result = User().apply {
name = "田中"
age = 20
}
println("私の名前は${result.name}です、${result.age}才です。")
// "私の名前は田中です、20才です。
- ブロック処理内では、レシーバーをthisで参照できる
- レシーバー自身を返す
詳しい解説はこちらをご覧ください。
alsoについて
alsoは、公式ドキュメントで以下のように定義されております。
- Kotlin
inline fun T.also(block: (T) -> Unit): T
【公式ドキュメント】 : also – Kotlin Programming Language
【使用例】
- Kotlin
class User {
var name: String = ""
var age: Int = 0
}
val result = User().also {
it.name = "伊藤"
it.age = 30
}
println("私は${result.name}、${result.age}です。")
// "私は伊藤、30才です。
- ブロック処理内では、レシーバーをitで参照できる
- レシーバー自身を返す
詳しい解説はこちらをご覧ください。
まとめ
- runは、ブロック{ }内の処理を実行する
- ブロック内の最後の処理を結果として返す
- runを呼び出すオブジェクトをレシーバーと呼ぶ
- ブロック内では、レシーバーをthisで呼び出せる
- runはレシーバーのnullチェックに便利
- ?. をつけた場合は、nullでなければ実効
- ?: をつけた場合は、nullであれば実効
関連記事