Preconditions入門〜引数や状態チェックをよりKotlinらしく〜

## はじめに
Kotlinで、関数の引数やデータの状態をチェックして不正であれば例外をスローしたい場面がありますよね。
今まで、何も考えずif文でチェックしてExceptionをthrowする書き方をしていました。

```kotlin
fun hello(user: String) {
    if (user.isEmpty()) {
        throw IllegalArgumentException("user must not be blank")
    }
    println("Hello, $user!")
}

// 空文字を渡すとException
hello("") // IllegalArgumentException: user must not be blank
```

しかし、Kotlinでは標準ライブラリである[Preconditions.kt](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Preconditions.kt)でこういったチェック処理をよりスマートに書ける関数群が提供されています。
この記事では、Preconditionsの使い方や使い分け、メリットなどをまとめていきます。

## require

### 定義

Exceptionのメッセージをデフォルトにする1.と、
カスタムメッセージを表示する2.のバリエーションがあります。（後述する`check`も同様です）

1. `require(value: Boolean): Unit`
1. `require(value: Boolean, lazyMessage: () -> Any): Unit`

### 使い方

[require](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html)は、valueがfalseの場合`IllegalArgumentException`をスローします。
冒頭のサンプルコードをrequireを使って書き直すと以下のようになります。
Kotlinらしくスッキリしましたね。

```kotlin
fun hello(user: String) {
    require(user.isNotEmpty()) { "user must not be blank" }
    println("Hello, $user!")
}

// 空文字を渡すとException
hello("") // IllegalArgumentException: user must not be blank
```

スローするのが`IllegalArgumentException`であることから、サンプルコードのように**引数を特定の条件でバリデーションする**ために使うことが想定されていると思います。

## requireNotNull

### 定義

1. `<T : Any> requireNotNull(value: T?): T`
1. `<T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T`

### 使い方

[requireNotNoll](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require-not-null.html)は、valueがnullかどうかを検証しnullの場合`IllegalArgumentException`をスローします。
`require`とのもう一つの違いとして、`requireNotNull`はvalueがtrueでExceptionが発生しない場合はnullableでない型として元の値を戻り値で返します。
そのため`requireNotNull`以降のロジックはnullでないことが保証された前提で書くことができます。
サンプルコードは以下です。（公式をアレンジ）

```kotlin
fun printRequiredParam(params: Map<String, String?>) {
    val required: String = requireNotNull(params["required"]) { "Required value must be non-null" } // returns a non-null value
    println(required)
    // ...
}

// 引数paramsに"required"に対応するアイテムが無く、nullを返す場合Exception
printRequiredParam(params) // IllegalArgumentException: Required value must be non-null
```

なるほど、引数に対するデータの存在チェックなどでnullが返る可能性のある時は`require`よりも`requireNotNull`が適していそうです。

## check

### 定義

1. `check(value: Boolean): Unit`
1. `check(value: Boolean, lazyMessage: () -> Any): Unit`

### 使い方

[check](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/check.html)は、valueがfalseの場合`IllegalStateException`をスローします。
サンプルコードは以下です。（公式をアレンジ）

```kotlin
// someStateは他の処理によって様々な値がセットされる
var someState: String = ""

fun getStateValue(): String {
    check(someState.isNotEmpty()) { "State must be non-empty" }
    // ...
    return someState
}

// someStateがブランクだとException
println(getStateValue()) // IllegalStateException: State must be non-empty
```

サンプルコードと、スローするのが`IllegalStateException`であることから、引数のチェックではなく何らかの**状態のバリデーション**に使われることが想定されていると思います。

## checkNotNull

### 定義
1. `<T : Any> checkNotNull(value: T?): T`
1. `<T : Any> checkNotNull(value: T?, lazyMessage: () -> Any): T`

### 使い方

[checkNotNull](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/check-not-null.html)は、`check`のNullチェックバージョンです。
nullableでない戻り値を返すのも、`require`と`requireNotNull`の違いと同じです。
サンプルコードは以下です。（公式をアレンジ）

```kotlin
// someStateは他の処理によって様々な値がセットされる
var someState: String? = null

fun getStateValue(): String {
    val state = checkNotNull(someState) { "State must be set beforehand" }
    // ...
    return state
}

// someStateがnullだとException
println(getStateValue()) // IllegalStateException: State must be set beforehand
```

## error

### 定義
`error(message: Any): Nothing`

### 使い方
[error](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/error.html)は、これ自体では何の条件もチェックせず与えられたメッセージで`IllegalStateException`をスローします。
サンプルコードは以下です。

```kotlin
fun classify(number: Number) {
    when (number) {
        1 -> println("1")
        2 -> println("2")
        else -> error("number is out of range")
    }
}

// whenで用意されていない条件を渡すとException
classify(3) // IllegalStateException: number is out of range
```

when式でelseをエラーにしたい時など、`require`や`check`単体で扱いにくいケースに役立ちそうです。


## 個人的に感じたPreconditionsを使うメリット

### シンプルで読みやすい

冒頭のifとthrowsを使ったサンプルコードのような書き方に比べて、Preconditionsを使った書き方は条件分岐が関数に内包されているためシンプルで読みやすいです。

### コードの意図が明確になる

読みやすいもう一つの理由は、コードの意図がより明確になるためです。ifとthrowsでやっていることを読むよりも、`require`や`check`という関数名とそのチェック条件を読むほうが「引数をチェックしている」「状態をチェックしている」という書き手の意図をより明示的に理解できるなと思いました。

また、これらの関数を適切な場面で使うことで、自動的にExceptionの種類も適切なものに決まるところもポイントが高いです。

ifとthrowsに比べてより宣言的と言い換えても良いかもしれません。

## まとめ
KotlinのPreconditionsの使い方をまとめてみました。
標準ライブラリを使いこなすことは「KotlinをKotlinらしく書く」ことに繋がると思います。
この記事が少しでも役に立てば嬉しいです。