contract?
長澤太郎さんによる、とてもわかりやすいスライドがすでにあるので見てください(完)
Kotlin Contract
で、終わるわけにもいけないので、具体的に試してみたことを書いてみます。
試してみること
- Nullableな引数を受け取って、Nullじゃないかつ、特定の状態であればtrueを返すメソッドを作成する
- このメソッドで判定を行った場合に、trueなら NonNullな型にスマートキャストされるようにする
具体的な例
nullかもしれない曜日の文字列を受け取って、休みの日(null ではない)なら hello ${曜日}
と出力する
/**
* 渡された曜日が休日かどうかを判定する
*
* @param dayOfWeek: "月", "火", "水", "木", "金", "土", "日", null
* @return true: 休日
* false: 休日ではない
*/
fun isHoliday(dayOfWeek: String?): Boolean {
return when (dayOfWeek) {
"日" -> true
else -> false
}
}
/** "hello ${曜日名}" を出力する */
fun helloHoliday(dayOfWeek: String) = println("hello $dayOfWeek")
// "日" を設定。ただしNullable 型としておく
val dayOfWeek: String? = "日"
if (isHoliday(dayOfWeek)) {
// 本当はこう書きたい
// helloHoliday(dayOfWeek)
// が、nullチェックしてからじゃないとコンパイルエラーになるため、どこかでnullチェックが必要
if (dayOfWeek != null) {
helloHoliday(dayOfWeek)
}
}
isHoliday が true を返したら null じゃないことがわかっているのに!という場合でも、helloHolidayの引数がNonNull型のため、nullチェックが必要です。
面倒くさい!なんとかしたい!
そんな時に使えるのがcontract
です。
isHoliday
を次のように書き換えます。
@ExperimentalContracts // contract はまだexperimental なので、annotationをつけておく必要がある
fun isHoliday(dayOfWeek: String?): Boolean {
// コンパイラに、 true を返したら、引数はnullではないですよ、と教えてやる
contract {
returns(true) implies (dayOfWeek != null)
}
return when (dayOfWeek) {
"日" -> true
else -> false
}
}
そうすると、次のような呼び出しでOKになります!
// 利用側も、どこかに@ExperimentalContracts をつけておく必要があるが今回は省略
if (isHoliday(dayOfWeek)) {
// こう書いてもコンパイルエラーにならない!
helloHoliday(dayOfWeek)
}
まとめ
- nullチェック + なんらかのチェック をまとめて行う関数を利用している場合、便利に使えそう
- ただし、まだExperimentalなので、利用する際は注意が必要
- 日曜日しか休みがないのはバグなのでは?