Kotlin の require, check, assert 関数の使い分け

いきなりまとめ

関数 用途
require 関数の引数のチェックに使う。
check 関数の引数以外のチェックに使う。
assert どこに使ってもよい。
実行時の VM 引数として -ea (-enableassertions) が必要。

背景

Kotlin には、引数として Boolean 型を取って、その引数が false であるときプログラムの実行を中止するような関数として require, check, assert の3つが存在する。本記事ではこれらの違いを述べる。 ついでにこれらの関数が何のために存在するのかも述べる。

"表明" という概念

プログラミングの重要な概念として「表明 (assertion)」という考え方がある。
ざっくり言うと、表明とは、プログラムの実行時に変数が満たすべき条件をコードとして表現することである。 たとえば「変数 i は負でない」とか「配列 a と配列 b の長さの合計は配列 c の長さに等しい」のような常に満たすべき条件をプログラムの一部として書く。成り立つべき条件を書いておけば、それが満たされない場合はエラーとして実行が中止されるため、以降の処理を安心して書ける。表明について Wikipedia のページ があるので詳しくはそれを参照のこと。

私はデバッグの用途で表明をよく使用する。 プログラムのあちこちに表明を書いておけば、バグの原因の切り分けが容易になることが多い。 また、ソースコード中になるべく "暗黙の前提" を残さないためにも、処理の節目に表明を入れておいたりする。こんな便利な表明であるが、Kotlin では表明に使用する関数として require, check, assert の3つがある。

それぞれの使い方を以下にまとめる。

require 関数

表明が満たされない場合 IllegalArgumentException が発生する。この例外は関数の引数が不正であることを示すためのものである。つまり、require 関数は、関数の引数に対する表明として用いるのが適切である。

require 関数の使用例は以下の通り。関数の引数である変数 count に対する表明を定義している。

fun getIndices(count: Int): List<Int> {
    require(count >= 0) { "Count must be non-negative, was $count" }
    // ...
    return List(count) { it + 1 }
}

ソースコードの引用元: require - Kotlin Programming Language

ちなみにエラーメッセージ { "Count must be non-negative, was $count" } の部分はなくてもよい。その場合は IllegalArgumentException のデフォルトのメッセージが設定される。

念のため説明しておくと{ "Count must be non-negative, was $count" } というのは
fun require(value: Boolean, lazyMessage: () -> Any) の第二引数 lazyMessage に対応している。Kotlin では最後の引数が関数であるとき、中括弧 { } でくくったラムダ式を引数の外に出せる。

つまり、上記のソースコード中の

    require(count >= 0) { "Count must be non-negative, was $count" }

    require(count >= 0, { "Count must be non-negative, was $count" })

と同じ意味である。

check 関数

表明が満たされない場合 IllegalStateException が発生する。この例外は 、リクエストされた処理を実行するために適切な状態になっていないことを示すものである。関数の引数のチェックは require 関数を用いるとして、それ以外のチェックを check 関数で行うような使い方になる。

check 関数の使用例は以下の通り。引数でない変数 state に対する表明を定義している。

var someState: String? = null
fun getStateValue(): String {
    val state = checkNotNull(someState) { "State must be set beforehand" }
    check(state.isNotEmpty()) { "State must be non-empty" }
    // ...
    return state
}

ソースコードの引用元: check - Kotlin Programming Language

assert 関数

プログラムの実行時の VM 引数として -ea (-enableassertions) を与えたときのみ有効になる。Javaassert キーワードと同様に、表明が満たされない場合は AssertionError を発生させる。Java の assert と同じ立場を取るなら、どんな種類の表明に用いてもよいと言える。

ドキュメント:assert - Kotlin Programming Language

ソースコードの例としては、上記のソースコードの require 関数や check 関数を assert 関数に置換したものを考えればよい。

メモ

VM 引数でオン/オフが切り替えられる点において、assert 関数は require 関数や check 関数と少し毛色が異なる。そのため、表明の扱いに対するプロジェクトの方針として、以下の2通りのいずれかになることが多いのではないかと思う。知らんけど。

  • require 関数と check 関数を使う
  • assert 関数のみ使う

とにかく、表明という考え方は実用的なので、標準ライブラリとしてサポートしている Kotlin は素晴らしい。