?: エルビス演算子
| 対応: | Kotlin 1.0(2016) |
|---|
Kotlinの『?:』(エルビス演算子)は、左辺がnullの場合に右辺の値を返す演算子です。nullのときのデフォルト値を簡潔に書けます。右辺に『return』や『throw』を書くことも可能です。
構文
// エルビス演算子(?: )
val length = name?.length ?: 0 // name が null なら 0
// return と組み合わせる
fun processName(name: String?) {
val nonNull = name ?: return // null なら関数を抜ける
println(nonNull.uppercase())
}
// throw と組み合わせる
fun requireName(name: String?): String {
return name ?: throw IllegalArgumentException("名前は必須です")
}
// チェーンで使う
val value = a?.b?.c ?: "デフォルト"
構文一覧
| 構文 | 概要 |
|---|---|
| a ?: b | a が null なら b を、null でなければ a を返します。 |
| ?: return | null のとき関数を早期リターンします。 |
| ?: throw 例外 | null のとき例外をスローします。 |
| ?: continue | ループ内で null のとき次の反復に進みます。 |
| ?: break | ループ内で null のときループを終了します。 |
サンプルコード
sample_elvis_operator.kt
fun getName(id: Int): String? = if (id == 1) "綾波レイ" else null
// エルビス演算子でデフォルト値を返す関数
fun greet(name: String?) {
val displayName = name ?: "名無し"
println("こんにちは、$displayName!")
}
// return と組み合わせる(ガード節パターン)
fun processUser(name: String?, age: Int?) {
val n = name ?: return // null なら即リターン
val a = age ?: return
println("$n(${a}歳)")
}
// throw と組み合わせる
fun divide(a: Int, b: Int?): Int {
val divisor = b ?: throw ArithmeticException("除数が null です")
return a / divisor
}
fun main() {
greet("碇シンジ") // こんにちは、碇シンジ!
greet(null) // こんにちは、名無し!
// 安全呼び出しとエルビス演算子の組み合わせ
val name1 = getName(1)
val name2 = getName(99)
println(name1?.length ?: -1) // 4
println(name2?.length ?: -1) // -1
// ガード節パターン
processUser("惣流アスカ", 14) // 惣流アスカ(14歳)
processUser(null, 14) // 何も出力されません
processUser("葛城ミサト", null) // 何も出力されません
// throw との組み合わせ
try {
println(divide(10, 2)) // 5
divide(10, null) // 例外スロー
} catch (e: ArithmeticException) {
println("例外: ${e.message}")
}
// チェーン呼び出しとエルビス演算子
data class Address(val city: String?)
data class User(val address: Address?)
val user: User? = User(Address(null))
val city = user?.address?.city ?: "不明"
println("都市: $city") // 不明
// コレクションでの活用
val names = listOf("綾波レイ", null, "碇シンジ", null, "惣流アスカ")
val result = names.map { it ?: "匿名" }
println(result) // [綾波レイ, 匿名, 碇シンジ, 匿名, 惣流アスカ]
}
elvis_operator.kt
kotlinc elvis_operator.kt -include-runtime -d elvis_operator.jar java -jar elvis_operator.jar こんにちは、碇シンジ! こんにちは、名無し! 4 -1 惣流アスカ(14歳) 5 例外: 除数が null です 都市: 不明 [綾波レイ, 匿名, 碇シンジ, 匿名, 惣流アスカ]
よくあるミス
NG例1: if/else で null チェックを書いてしまい冗長になる。
mistake1_verbose_null_check.kt
fun greet(name: String?) {
val displayName = if (name != null) name else "名無し" // 冗長な書き方
println("こんにちは、$displayName!")
}
fun main() {
greet(null)
}
コンパイルして実行すると次のようになります。
kotlinc mistake1_verbose_null_check.kt -include-runtime -d mistake1_verbose_null_check.jar java -jar mistake1_verbose_null_check.jar こんにちは、名無し!
エルビス演算子で簡潔に書ける。
fix1_elvis.kt
fun greet(name: String?) {
val displayName = name ?: "名無し"
println("こんにちは、$displayName!")
}
fun main() {
greet("碇シンジ")
greet(null)
}
コンパイルして実行すると次のようになります。
kotlinc fix1_elvis.kt -include-runtime -d fix1_elvis.jar java -jar fix1_elvis.jar こんにちは、碇シンジ! こんにちは、名無し!
NG例2: ?: の左辺が非 null 型の場合、右辺は絶対に使われない(警告またはコンパイルエラーになる)。
mistake2_non_nullable_lhs.kt
fun getLabel(age: Int): String {
return age.toString() ?: "不明" // age は非 null 型なので ?: は絶対に評価されない
}
NG例3: ?: は null のみに反応する — 空文字列はデフォルトにならない。
mistake3_empty_string.kt
fun showPilot(name: String?) {
val display = name ?: "パイロット未定"
println(display)
}
fun main() {
showPilot("") // 空文字列がそのまま表示される("パイロット未定" にならない)
showPilot(null) // パイロット未定
}
コンパイルして実行すると次のようになります。
kotlinc mistake3_empty_string.kt -include-runtime -d mistake3_empty_string.jar java -jar mistake3_empty_string.jar パイロット未定
空文字列も含めてデフォルトにしたい場合は takeIf や isNullOrBlank を使う。
fix3_null_or_blank.kt
fun main() {
val name: String? = ""
val result = name?.takeIf { it.isNotBlank() } ?: "綾波レイ"
println(result)
}
コンパイルして実行すると次のようになります。
kotlinc fix3_null_or_blank.kt -include-runtime -d fix3_null_or_blank.jar java -jar fix3_null_or_blank.jar 綾波レイ
概要
エルビス演算子は三項演算子の代わりに使えるKotlin独自の演算子です。『name ?: "デフォルト"』はJavaの『name != null ? name : "デフォルト"』と同等ですが、より簡潔に書けます。
右辺に『return』や『throw』を書くことで「nullなら早期リターン」「nullなら例外」のガード節パターンを簡潔に実装できます。これは関数の先頭でのnull検証に非常に有効です。
Nullable型の基本はNullable 型 / ?. 演算子を、強制的なnull排除は!! 非 null アサーションを参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。