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を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。