Edited at

Kotlin スコープ関数の使い分け


まとめ

使い分けのポイントはたったの2つだけ!


  • 対象のオブジェクトのブロック内引数を it にしたいか」 or this にしたいか」

  • 戻り値を「自分で指定したいか」 or 「対象のオブジェクトにしたいか」


はじめに

スコープ関数をプロジェクトで使用する頻度が増えてきたので、この機会に私なりの見解でまとめてみました。

繰り返しになりますが、意識するポイントは「引数」「戻り値」の2つだけです。

(let, run, also, apply それぞれの定義についてはまとめてコチラに載せました。)

(また、使用頻度が低いため with については省略をしました。)


引数

ポイントは、

「ブロック内の引数を、 it にするか this にするか」

です。

それぞれの特長は以下のようです。


it


  • 名前を変えられるので引数の意味がわかりやすくなる。

val user = User()

nameTxt.text = user.name
ageTxt.text = user.age.toString()
hobbyTxt.text = user.hobby

// data class User(
// val name: String = "Taro",
// val age: Int = 20,
// val hobby: String = "Baseball"
// )
// nameTxt, ageTxt, hobbyTxtはそれぞれTextViewのid

これが、

User().also { person ->

nameTxt.text = person.name
ageTxt.text = person.age.toString()
hobbyTxt.text = person.hobby
}

こうなる。

thisの方でもいけますが、感覚的にはこんな感じです。

(もっとわかりやすい例があると思いますが、すみません。)


this



  • thisを省略できる。

val toast = Toast.makeText(this, "message", Toast.LENGTH_SHORT)

toast.setGravity(Gravity.CENTER, 0, 0)
toast.setMargin(10.0f, 10.0f)
toast.show()

これが、

Toast.makeText(this, "message", Toast.LENGTH_SHORT).apply {

setGravity(Gravity.CENTER, 0, 0)
setMargin(10.0f, 10.0f)
show()
}

こうなる。

toast が何回も表示されていたのが、スッキリしました。

以上より、

名前を決めたいなら it,

it.method1()

it.method2()

のようになるのであれば、this (の省略)を使って

method1()

method2()

とするのがいいのではないかと思います。


戻り値

ポイントは、

「戻り値を、 自分で指定したもの にするか 対象のオブジェクト にするか」

です。

感覚的には以下のようになります。


自分で指定する

xxxxx.let {

aaaaa = it
it.bbbbb
ccccc(it) <- 戻り値はコレ(ccccc(it))になる
}


対象のオブジェクト

  ⬇ 戻り値はコレ(xxxxx)になる

xxxxx.also {
aaaaa = it
it.bbbbb
ccccc(it)
}

スコープ関数を使い始めのときはこのことがわかりませんでした。。。

しかし、このことがわかってからは

「Nullableのオブジェクトはletを使って処理すると良い」

ということの意味がようやくわかりました。


練習問題(1)

※コードの意味は特にありません。

val intent = Intent(this, SecondActivity::class.java)

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addCategory("sample")
startActivity(intent)

まとめの表に従うと、

まず、戻り値は必要でないので、対象のオブジェクトにしましょう。

引数はどちらでもいいと思いますが、個人的にはthisですかね。

よって、applyを使います。

Intent(this, SecondActivity::class.java).apply {

addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addCategory("sample")
startActivity(this)
}


練習問題(2)

※こちらも特にコードに意味はありません。

val contents = getContents()

val master =
if (contents != null) {
println(contents)
contents
} else {
throw Exception()
}

private fun getContents(): String? = "Contents"

この場合は戻り値が必要なので、自分で指定できる方を選択します。

引数はthisだと内容がわかりにくくなるので、it

よってletを使用します。すると、以下のように記述できます。

val master = getContents()?.let { contents ->

println(contents)
contents
} ?: throw Exception()

どうでしょうか?かなりスッキリ書けたのではないでしょうか。

スコープ関数万歳!(エルビス演算子の効果もありますが。)


定義(まとめ)

以下のようにまとまって定義が載ってるサイトが少なかったので、比較しやすいようにまとめて載せました。是非、感覚的にイメージを掴んだ後にコードに立ち戻って定義を理解しましょう。

public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

public inline fun <T, R> T.run(block: T.() -> R): R = block()

public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}

public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}


おわりに

Kotlinのスコープ関数使いたがり症候群でちゃう。


参考

https://qiita.com/ngsw_taro/items/d29e3080d9fc8a38691e