言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. also

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』が適しています。

オブジェクトの初期化にはapplyを、null チェックにはletを参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。