突然ですが問題です!
なにがログに出力されるか答えてください。
第一問
//
fun test1() {
var hoge: String? = "hoge"
var hage: String? = "hage"
hoge?.let { ho ->
hage?.let { ha ->
Log.d("test1", "$ho is $ha")
}
} ?: run {
Log.d("test1", "hoge is null")
}
}
hogeとhageがnullじゃないパターン。
これは簡単だよね。何も考えなくても答えがわかると思う。
答え
test1: hoge is hage
まぁ、そうだよね。両方共nullじゃないんだし、全部出力されるよね。
第二問
//
fun test2() {
var hoge: String? = null
var hage: String? = "hage"
hoge?.let { ho ->
hage?.let { ha ->
Log.d("test2", "$ho is $ha")
}
} ?: run {
Log.d("test2", "hoge is null")
}
}
hogeがnullのパターン。
まぁ、これも簡単だと思う。
答え
test2: hoge is null
うん。hogeはnullだし、これが出力されるよね。
第三問
//
fun test3() {
var hoge: String? = "hoge"
var hage: String? = null
hoge?.let { ho ->
hage?.let { ha ->
Log.d("test3", "$ho is $ha")
}
} ?: run {
Log.d("test3", "hoge is null")
}
}
hageがnullのパターン。
順当に行けば、hageのletの?:runが無いし、何も表示されないと思うよね。
思ってたんだよね。これがタイトルに繋がるんだよ。
答え
test3: hoge is null
おぉぉぉ・・・・??
hogeがnullじゃないのに「hoge is null」って表示されてしまう。
なんでこんな事が起こるのか
letは最後の評価を返すのでこの場合、hage?.letはnullを返している。
結果、hoge?.letがnull評価されてしまい「hoge is null」が 出力されてしまうってわけ。
予期しないところでnull評価されちゃって変なバグを生む可能性があるのでlet ?: runを使うのを辞めないか?って言うわけ。
どうするか
極力letは使わないようにすれば良いと思う。
個人的に普段の実装はあまりletは使っていない。
letは使わずにalsoを使えば良いと思う。
//
fun test4() {
var hoge: String? = "hoge"
var hage: String? = null
hoge?.also { ho ->
hage?.let { ha ->
Log.d("test4", "$ho is $ha")
}
} ?: run {
Log.d("test4", "hoge is null")
}
}
alsoは何も返さないのでこの場合は何も表示されない。
この場合は本当にhogeがnullの場合にしか 「hoge is null」 と表示されない。