言語
日本語
English

Caution

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

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. let でスコープ限定

let でスコープ限定

対応: Kotlin 1.0(2016)

Kotlinの『?.let』は、Nullable型の値がnullでない場合のみラムダを実行するパターンです。nullチェックと処理をワンライナーで書けます。スコープ内では変数『it』でnon-null値にアクセスできます。

構文

// ?.let で null のときはスキップする
val name: String? = "sample_data"
name?.let {
    println(it.length) // null でなければ実行される
}

// 結果を受け取る(null の場合は null が入る)
val result: Int? = name?.let { it.length * 2 }

// if null チェックとの比較
if (name != null) {
    println(name.length) // スマートキャスト
}
// 上記と同等:
name?.let { println(it.length) }

構文一覧

構文概要
value?.let { it }value が null でなければラムダを実行し、結果を返します。null なら null を返します。
value?.let { v -> }引数名を明示して受け取ります。
?.let と ?: の組み合わせnull 時のデフォルト値を設定します(value?.let { 処理 } ?: デフォルト)。
?.also { it }let と似ていますが、レシーバー自身を返します(副作用に使います)。

サンプルコード

sample_let_null_check.kt
data class User(val name: String, val email: String?)

fun findUser(id: Int): User? = if (id == 1) User("user_1", "user_1@wp-p.info") else null

fun main() {
    // 基本的な ?.let の使い方
    val name: String? = "Kotlin"
    name?.let {
        println("長さ: ${it.length}") // 長さ: 6
        println("大文字: ${it.uppercase()}") // 大文字: KOTLIN
    }

    // null の場合はスキップされる
    val nullName: String? = null
    nullName?.let { println("これは実行されません") }
    println("null をスキップしました") // null をスキップしました

    // 結果を受け取る
    val length: Int? = name?.let { it.length * 2 }
    println("2倍の長さ: $length") // 2倍の長さ: 12

    // ?:(エルビス)と組み合わせる
    val nullLength: Int = nullName?.let { it.length } ?: -1
    println("null 時のデフォルト: $nullLength") // -1

    // ネストした Nullable をチェーンで処理する
    val user: User? = findUser(1)
    user?.let { u ->
        u.email?.let { email ->
            println("メール: $email") // メール: user_1@wp-p.info
        }
    }

    // findUser(99) は null なのでスキップされる
    findUser(99)?.let { println("ユーザー: ${it.name}") }
    println("存在しないユーザーをスキップしました")

    // リストの null 要素を処理する
    val names: List<String?> = listOf("item_a", null, "item_b", null, "item_c")
    names.forEach { it?.let { name -> println(name) } }
}
let_null_check.kt
kotlinc let_null_check.kt -include-runtime -d let_null_check.jar
java -jar let_null_check.jar
長さ: 6
大文字: KOTLIN
null をスキップしました
2倍の長さ: 12
null 時のデフォルト: -1
メール: user_1@wp-p.info
存在しないユーザーをスキップしました
item_a
item_b
item_c

よくあるミス

ミス1: 『?.let』の戻り値は Nullable 型(『Int?』など)なので、そのまま算術演算に使えません。

mistake_let_nullable_return.kt
fun main() {
    val nullName: String? = null
    val len = nullName?.let { it.length } // len は Int? 型
    // println(len + 1) // コンパイルエラー: Int? に + は使えない
}

『?:』でデフォルト値を指定すると non-null の型になります。

fix_let_nullable_return.kt
fun main() {
    val nullName: String? = null
    val safeLen: Int = nullName?.let { it.length } ?: 0
    println("長さ: $safeLen") // 長さ: 0
}

ミス2: ネストした『?.let』の中で引数名を省略すると、複数の『it』が重なって読みにくくなります。

mistake_let_it_nested.kt
fun main() {
    val name: String? = "sample_data"
    // it が二重になって混乱しやすい
    name?.let { it?.let { it.uppercase() } }
}

ネストするときは引数名を明示します。

fix_let_it_nested.kt
fun main() {
    val name: String? = "sample_data"
    name?.let { n -> println(n.uppercase()) }
}

ミス3: 『let』はラムダの最後の式を返しますが、『also』はレシーバー自身を返します。混同すると意図しない型になります。

mistake_let_vs_also.kt
fun main() {
    val name: String? = "item_a"
    val result1 = name?.let { it.length } // Int?(変換結果)
    val result2 = name?.also { it.length } // String?(元の値、変換されない)
    println("let: $result1") // let: 6
    println("also: $result2") // also: item_a
}

コンパイルして実行すると次のようになります。

kotlinc mistake_let_vs_also.kt -include-runtime -d mistake_let_vs_also.jar
java -jar mistake_let_vs_also.jar
let: 6
also: item_a

概要

『?.let』はKotlinで最もよく使われるNullチェックパターンの1つです。『if (value != null) { ... }』と同等ですが、スコープ関数として扱えるため、結果を変数に代入したりチェーンしたりできます。

スコープ内のパラメーター名は既定で『it』ですが、『?.let { user -> ... }』のように名前を付けるとネスト時に読みやすくなります。

Nullable型の基本はNullable 型 / ?. 演算子を、スコープ関数全般はletを参照してください。

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