この記事はMicroAd Advent Calendar 2019の1日目の記事です。
状況
「List
やSet
と言ったCollection
から要素を1つ取り出す」場合、get
メソッドやfirst
関数を使いたくなります。
val list: List<String> = listOf("a", "b", "c")
val first: String? = list.first { it == "d" }
val secondElement: String? = list.get(1) // 実際は[]を用いることができるが、説明のためgetを用いる
しかし、これらの処理は、該当する要素が見つからなかった(e.g. empty
だった、検索条件に引っかからなかった、要素以上のインデックスだった、等)場合、それぞれNoSuchElementException
とIndexOutOfBoundsException
を投げます。
つまり、「見つからなかったらnull
にする(or した上でnull
だった時の処理を入れる)」的な処理に使うことはできません。
サンプルコードの通り、これらの取得結果をnullable
型に代入しても警告やエラーにならないため、深いバグになりやすい点でも注意が必要です。
対処
(条件分岐や例外処理をしない形での)Collection
の要素を安全な取り出しは、null
やデフォルト値を返す関数を用いることで実現できます。
ここでは具体的に以下の3種類を紹介します。
-
~OrElse
系 -
~OrNull
系 -
find
系
「~
系」としてまとめていますが、これらは関数によって有ったりなかったりします。
その具体的な有無に関してはソースコードをご覧下さい。
~OrElse
系関数を用いる
見つからなければデフォルト値を返すのが~OrElse
系関数です。
getOrElse
やelementAtOrElse
系が有ります。
この~OrNull
系関数は、以下のように、デフォルトの値生成にInt -> デフォルト値
のラムダを取ります。
val index // 取得対象インデックス想定
list.getOrElse(index) { i -> // iにはindexが入ってくる
// デフォルト値の生成処理
}
~OrNull
系関数を用いる
見つからなければnull
を返すのが~OrNull
系関数です。
firstOrNull
やgetOrNull
などが有ります。
find
系関数を用いる
~OrNull
系関数と同じく、見つからなければnull
を返すのがfind
系関数です。
find
とfindLast
関数が有ります。
内容的にはそれぞれfirstOrNull
とlastOrNull
を呼び出しています。
終わりに
この記事では条件分岐や例外処理をしない形でCollection
の要素を安全に取り出す方法についてまとめました。
この記事を書いたきっかけは、チームの人間が何人も「Java
のStream API
のfindFirst
メソッドとfirst
関数の挙動が違う」という罠を踏んだことです。
しかも前述の通りnullable
型に代入しても警告やエラーにならないため、ヒヤッとする所に取り残してしまうことも有りました。
get
系はともかく、何か検索して取ってくるような処理では「見つからなければnull
またはデフォルト値」を返すだけでいい気がするんですが、何でそうなっていないんでしょうか……。
ともあれ、この記事がCollection
の要素を安全に取り出すことに役立てば幸いです。