0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】inline + reified + sealed class を組み合わせた型安全 Result<T> 設計

Last updated at Posted at 2025-10-09

はじめに

Kotlin で非同期処理や API 呼び出しを書くとき、
成功・失敗を明確に扱う「Result型」パターンはよく使われます。

val result = runCatching { fetchData() }

しかし、標準の Result クラスは少し扱いづらく、
例外スタックの可読性や再利用性に課題があります。

この記事では、
sealed class × inline × reified の3要素を組み合わせて、
型安全・拡張性・再利用性 に優れた Result<T> を自作します。


Step 1. sealed class で結果構造を定義

まずは Kotlin らしく「結果の網羅性」を保証する sealed class を作成します。

sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Failure(val exception: Throwable) : Result<Nothing>()
}
  • Success:成功データを保持
  • Failure:例外を保持
  • sealed class により when 式で網羅性チェックが可能

Step 2. inline 関数で安全な呼び出しを設計

次に、例外を安全に包み込む runSafe 関数を作ります。

inline fun <T> runSafe(block: () -> T): Result<T> {
    return try {
        Result.Success(block())
    } catch (e: Exception) {
        Result.Failure(e)
    }
}

使用例

val result = runSafe { 10 / 2 }
val error = runSafe { 10 / 0 }

println(result) // Success(data=5)
println(error)  // Failure(exception=java.lang.ArithmeticException)

→ try-catch を呼び出し側から完全に排除できます。


Step 3. reified 型パラメータで型安全にキャスト

Failure に格納された例外を型安全に処理したいとき、
reified 型パラメータを使うと便利です。

inline fun <reified E : Throwable> Result<*>.onErrorOfType(action: (E) -> Unit): Result<*> {
    if (this is Result.Failure && exception is E) {
        action(exception as E)
    }
    return this
}

使用例

runSafe { 10 / 0 }
    .onErrorOfType<ArithmeticException> { e ->
        println("算術エラー: ${e.message}")
    }
    .onErrorOfType<IllegalStateException> { e ->
        println("状態エラー: ${e.message}")
    }

reified により E::classis E が実行時に判定可能。
→ 複雑なエラーハンドリングも型安全に実装できます。


Step 4. map / flatMap を追加して再利用性UP

Result を monad 的に扱えるように、map / flatMap を定義しましょう。

inline fun <T, R> Result<T>.map(transform: (T) -> R): Result<R> =
    when (this) {
        is Result.Success -> runSafe { transform(data) }
        is Result.Failure -> this
    }

inline fun <T, R> Result<T>.flatMap(transform: (T) -> Result<R>): Result<R> =
    when (this) {
        is Result.Success -> transform(data)
        is Result.Failure -> this
    }

使用例

runSafe { "123" }
    .map { it.toInt() }
    .flatMap { runSafe { it * 2 } }
    .onErrorOfType<NumberFormatException> { println("数値変換エラー") }
    .let { println(it) } // → Success(data=246)

Step 5. when 式での安全な状態処理

sealed class により、when 式で網羅的に扱えます。

fun handleResult(result: Result<String>) {
    when (result) {
        is Result.Success -> println("成功: ${result.data}")
        is Result.Failure -> println("失敗: ${result.exception.message}")
    }
}

else が不要、コンパイラレベルで漏れのない分岐 が保証されます。


Step 6. まとめ:3要素の役割

要素 役割 効果
sealed class 結果を Success / Failure に限定 型網羅性・安全性
inline ラムダをインライン展開 パフォーマンス最適化・try 範囲の明確化
reified 実行時に型情報を保持 型安全なエラーハンドリング

Bonus:汎用ネットワーク呼び出しに応用

inline fun <reified E : Throwable, T> safeApiCall(
    crossinline block: suspend () -> T,
    crossinline onError: (E) -> Unit = {}
): Result<T> = try {
    Result.Success(block())
} catch (e: Exception) {
    if (e is E) onError(e)
    Result.Failure(e)
}

使用例(suspend 関数内で)

val response = safeApiCall<HttpException, String>(
    block = { api.getUserData() },
    onError = { println("HTTPエラー: ${it.code()}") }
)

→ Retrofit + Coroutine 環境で、
例外の型ごとに安全に処理を分けられます。


まとめ

特徴 内容
sealed class 結果の型を明示・網羅性チェック可能
inline try-catch コスト削減 & パフォーマンス向上
reified 実行時型情報を利用した安全な例外処理
応用 Intent・API呼び出し・ViewModel処理などで型安全設計を実現

終わりに

sealed class × inline × reified の組み合わせは、
Kotlinで「例外処理をロジックから消し去る」ための最強トリオです。

  • 型安全に結果を表現
  • when式で完全網羅
  • パフォーマンスも最適化

この構造をベースにすれば、
アプリ全体の「エラーハンドリング」を統一し、
Clean ArchitectureMVI にも自然に統合できます。


参考

  • Kotlin Docs: Inline and reified
  • Effective Kotlin: Item 35 – Prefer sealed classes to enums

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?