📑

Kotlinの関数型エラーハンドリング例

に公開

この記事は、自分の技術メモをAIで記事化しているため、一部間違いが含まれている可能性があります。ご了承ください!

Kotlinの関数型エラーハンドリングについて、実践的なコード例を交えながら解説します。本記事では、特にエラー定義や初期化時の注意点について掘り下げていきます。

💡 エラーハンドリングの基本構造

Kotlinでは、関数型のアプローチでエラーを扱うことができます。エラー定義に情報を付加することで、エラーメッセージの管理が楽になります。

エラー定義のポイント

  1. エラーメッセージをメソッド化する

    毎回エラーメッセージを手動で組み立てるのは非効率です。メソッドにして再利用性を高めましょう。

  2. エラー情報を付加する

    エラーの種類に応じて、追加の情報(例: サイズなど)を持たせると便利です。

以下のコード例では、CommentBodyFailureというエラー定義を作成しています。

package com.example.comment

import com.example.util.Result
import com.example.util.Result.Success
import com.example.util.Result.Failure

sealed interface CommentBodyFailure {
    // 毎回エラーメッセージを組み立てるのは無駄なので、メソッドにしておく
    fun errorMessage(): String

    data class TooLarge(val actualSize: Int) : CommentBodyFailure {
        override fun errorMessage() = "Too large for $actualSize"
    }

    data class TooSmall(val actualSize: Int) : CommentBodyFailure {
        override fun errorMessage() = "Too small for $actualSize"
    }
}

🛠️ データクラスと初期化処理の注意点

データクラスにおける初期化処理では、不変条件を守るためにinitブロックを利用します。ただし、constructorを公開しておくことで初期化テストがしやすくなります。

初期化時の注意点

  • 不変条件の記述

    initブロックでデータの妥当性をチェックします。

  • テストの重複

    constructorvalidate関数の両方でテストが必要になる点に注意。

以下は、CommentBodyクラスの例です。

data class CommentBody constructor(private val value: String) {
    init {
        // 不変条件としてinitも書いておく
        // デメリットとしては、constructorとvalidate関数のテストが重複して必要になること
        validate(value)?.let {
            // 特に種別ごとにハンドリングする必要ないなら、これでいい
            // ハンドリングしたいならwhenを使ってハンドリングすると良さそう
            throw IllegalArgumentException(it.errorMessage())
        }
    }

    companion object {
        const val MAX_LENGTH = 100_000
        const val MIN_LENGTH = 1

        private fun validate(value: String): CommentBodyFailure? {
            val length = value.length

            if (length < MIN_LENGTH) {
                return CommentBodyFailure.TooSmall(actualSize = length)
            }

            if (length > MAX_LENGTH) {
                return CommentBodyFailure.TooLarge(actualSize = length)
            }

            return null
        }

        fun create(value: String): Result<CommentBody, CommentBodyFailure> =
            validate(value)
                ?.let { Failure(it) }
                ?: Success(CommentBody(value))
    }
}

📌 実装時のポイント

  • エラーハンドリングの簡略化

    エラー種別ごとに個別対応が不要なら、そのままスローする設計が有効です。

  • 再利用性の高い設計

    バリデーション処理をcompanion objectに切り出すことで、簡単に再利用できます。


Kotlinを使った関数型エラーハンドリングの基本構造を理解することで、より堅牢でメンテナンス性の高いコードを書くことができます。今回のコード例を参考に、ぜひ実践で試してみてください!

Discussion