Kotlin
KotlinDay 8

contract を試してみる


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なので、利用する際は注意が必要

  • 日曜日しか休みがないのはバグなのでは?