Kotlinでif let elseをやりたいときはletでなくalsoを使おう

2018/12/21 2839hit
このエントリーをはてなブックマークに追加

この記事はAndroid Advent Calendar 2018の15日めです。
え?前回の日記は16日目だったのになぜ 今日は15日目かだって? それは言わないお約束です。

Swiftには次のように書くことで、
hogeが非nullだった場合はif内の処理、nullだった場合はelseの処理を行う仕組みがあります。


if let hoge = hoge.val {
// hogeがnullでないときだけ実行
} else {
// hogeがnullのときだけ実行
}

似たようなことをKotlinでやろうとするとこんな感じでやりがち

hoge?.let{hoge->
// hogeがnullでないときだけ実行
}

定形ですね。

ところが、この方法ではelseを指定することが出来ません。

そこで、
ついうっかりエルビス演算子を使ってしまいがちです。



hoge?.let{hoge->
// hogeがnullでないときだけ実行
}?:run{
// hogeがnullのときだけ実行
}

試しに実行してみましょう。
hogeがnullのとき

var hoge: String? = null

hoge?.let{
System.out.println("not null")
} ?: run{
System.out.println("null")
}
// null


hogeが非nullのとき

var hoge: String? = "hogehoge"

hoge?.let{
System.out.println("not null")
} ?: run{
System.out.println("null")
}
// not null


良さそうです。

嘘です 良くないです。

問題

この方式には明確な問題点があります。 エルビス演算子は左辺がnullの場合のみ右辺を評価します。
?.はnullではないときだけ以降の処理を行い、nullのときはnullを返します。 なので一見うまく動いているように見えます。

ところが、問題はletの戻り値です。
nullのアンラップのためにletを使っていると忘れそうになりますが、letは最後の処理結果を戻り値として戻します。
それが何故問題になるかというと、
こういう処理を書くとバグります。
hogeは非nullだけれど nullとも出力される。

var hoge: String? = "hogehoge"

hoge?.let{
System.out.println("not null")
null
} ?: run{
System.out.println("null")
}
// not null
// null


letの最後でnullがあるのでエルビス演算子の左辺がnullとなってしまい右辺が評価されます。
ここまであからさまにnullを書くことはないかもしれませんが、最後の処理がnullを戻す処理だった場合は同様の問題が発生します。

対策


冒頭のタイトルに有るようにletではなくalsoを使いましょう。

hoge?.also{
System.out.println("not null")
null
} ?: run{
System.out.println("null")
}
// not null


letとalsoの違いは戻り値の違いでletはlet内で最後に評価した値を戻り値とするのに対してalsoはその元になったオブジェクト(ここではhoge)の値が戻ります。
hogeが非nullの場合はalsoの戻り値もnullになりえないのであとのrunを誤爆する恐れがありません

参考
Kotlin スコープ関数 用途まとめ

あとがき


そもそも if elseを使ったほうが見やすいのでは? という話もある。


if(hoge != null){
System.out.println("not null")
}else{
System.out.println("not null")
}

前:複数のViewTypeをもつRecyclerViewをSealedClassでシンプルに作る 次:2018年 今年買ってよかったもの

関連キーワード

[Android][Java][モバイル][IT]

コメントを投稿する

名前URI
コメント