also
| 対応: | Kotlin 1.0(2016) |
|---|
Kotlinのスコープ関数『also』は、オブジェクトに対して副作用(ログ出力・デバッグ・追加処理)を挿入したいときに使います。ブロック内では『it』でオブジェクトを参照でき、戻り値はレシーバー自身です。
構文
// also の基本形
val result = オブジェクト.also {
// it = オブジェクト
println(it) // 副作用(ログなど)
}
// result はオブジェクト自身
構文一覧
| スコープ関数 | コンテキスト参照 | 戻り値 | 用途 |
|---|---|---|---|
| also { } | it | レシーバー自身 | 副作用(ログ・デバッグ)の挿入に使います。 |
| apply { } | this(省略可) | レシーバー自身 | オブジェクトの設定・初期化に使います。 |
| let { } | it | ラムダの結果 | null チェックや変換チェーンに使います。 |
| run { } | this(省略可) | ラムダの結果 | オブジェクトのセットアップと計算を組み合わせます。 |
サンプルコード
sample_scope_also.kt
fun main() {
// also でメソッドチェーンの途中にログを挿入します
val numbers = mutableListOf(1, 2, 3)
.also { println("変換前: $it") } // 変換前: [1, 2, 3]
.map { it * 2 }
.also { println("変換後: $it") } // 変換後: [2, 4, 6]
println(numbers) // [2, 4, 6]
// also は自身を返すため、処理を邪魔せずログを差し込めます
val text = "Hello, Kotlin!"
.also { println("文字数: ${it.length}") } // 文字数: 14
.uppercase()
println(text) // HELLO, KOTLIN!
// バリデーションの挿入にも使えます
data class User(val name: String, val age: Int)
fun createUser(name: String, age: Int): User {
return User(name, age).also {
require(it.age >= 0) { "年齢は0以上が必要です" }
println("ユーザー作成: $it")
}
}
val user = createUser("user_x", 28)
println(user)
// ユーザー作成: User(name=user_x, age=28)
// User(name=user_x, age=28)
}
scope_also.kt
kotlinc scope_also.kt -include-runtime -d scope_also.jar java -jar scope_also.jar 変換前: [1, 2, 3] 変換後: [2, 4, 6] [2, 4, 6] 文字数: 14 HELLO, KOTLIN! ユーザー作成: User(name=user_x, age=28) User(name=user_x, age=28)
よくあるミス
NG例1: also の戻り値はレシーバー自身です — ブロック内の変換結果は返されません
NG例2: also のブロック内では this ではなく it でオブジェクトを参照します
NG例3: apply と also を混同して、also でプロパティを設定しようとする — 動作はしますが意図が伝わりにくくなります
fun main() {
val numbers = listOf(1, 2, 3)
// also の戻り値はレシーバー自身のため map の結果は捨てられる
val doubled = numbers.also { it.map { n -> n * 2 } }
println(doubled) // [1, 2, 3](変換されていない)
}
変換が目的なら map や let を使います
sample_scope_also_mistakes.kt
fun main() {
val numbers = listOf(1, 2, 3)
val doubledCorrect = numbers.map { it * 2 }
println(doubledCorrect) // [2, 4, 6]
// also のブロック内では it でアクセスする
val text = "Hello, Kotlin"
text.also { println(it.length) } // 13
// オブジェクト設定には apply の方が意図が伝わりやすい
data class Config(var host: String = "", var port: Int = 0)
val config = Config().apply {
host = "localhost"
port = 8080
}
println(config) // Config(host=localhost, port=8080)
}
コンパイルして実行すると次のようになります。
kotlinc sample_scope_also_mistakes.kt -include-runtime -d sample_scope_also_mistakes.jar java -jar sample_scope_also_mistakes.jar [2, 4, 6] 13 Config(host=localhost, port=8080)
概要
『also』の最大の特徴は、処理の流れを変えずに副作用だけを追加できる点です。メソッドチェーンの途中でデバッグログを挿入したり、バリデーションを追加するときに活躍します。
『also』と『apply』はどちらもレシーバー自身を返しますが、コンテキスト参照が『it』(also)と『this』(apply)で異なります。オブジェクトの外からの操作は『also』、オブジェクト自身の設定は『apply』が適しています。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。