2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

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

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でこういったチェック処理をよりスマートに書ける関数群が提供されています。
この記事では、Preconditionsの使い方や使い分け、メリットなどをまとめていきます。

require

定義

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

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

使い方

requireは、valueがfalseの場合IllegalArgumentExceptionをスローします。
冒頭のサンプルコードをrequireを使って書き直すと以下のようになります。
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
  2. <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T

使い方

requireNotNollは、valueがnullかどうかを検証しnullの場合IllegalArgumentExceptionをスローします。
requireとのもう一つの違いとして、requireNotNullはvalueがtrueでExceptionが発生しない場合はnullableでない型として元の値を戻り値で返します。
そのためrequireNotNull以降のロジックはnullでないことが保証された前提で書くことができます。
サンプルコードは以下です。(公式をアレンジ)

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
  2. check(value: Boolean, lazyMessage: () -> Any): Unit

使い方

checkは、valueがfalseの場合IllegalStateExceptionをスローします。
サンプルコードは以下です。(公式をアレンジ)

// 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
  2. <T : Any> checkNotNull(value: T?, lazyMessage: () -> Any): T

使い方

checkNotNullは、checkのNullチェックバージョンです。
nullableでない戻り値を返すのも、requirerequireNotNullの違いと同じです。
サンプルコードは以下です。(公式をアレンジ)

// 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は、これ自体では何の条件もチェックせず与えられたメッセージでIllegalStateExceptionをスローします。
サンプルコードは以下です。

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をエラーにしたい時など、requirecheck単体で扱いにくいケースに役立ちそうです。

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

シンプルで読みやすい

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

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

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

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

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

まとめ

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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?