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】Kotlinにおける例外処理(Exception Handling)

Posted at

概要

Kotlin の例外処理は、Java の仕組みを継承しつつも、より安全で柔軟です。
特に、try-catch 構文だけでなく、runCatchingResult、コルーチン専用の CoroutineExceptionHandler など、モダンな設計パターンが利用できます。

この記事では以下の観点から体系的に紹介します:

  1. try-catch-finally の基本構文
  2. throw / Throwable / Exception の階層
  3. Kotlin独自の runCatching / Result API
  4. コルーチンの例外処理 (SupervisorJob, CoroutineExceptionHandler)
  5. 実戦設計パターン(安全なエラーハンドリング構造)

1. 基本構文:try-catch-finally

Kotlin では、例外処理は次のように書けます:

fun divide(a: Int, b: Int): Int {
    return try {
        a / b
    } catch (e: ArithmeticException) {
        println("Error: ${e.message}")
        0 // 代替値を返す
    } finally {
        println("処理終了")
    }
}

fun main() {
    println(divide(10, 2))  // => 5
    println(divide(10, 0))  // => 0
}

ポイント:

  • try は式(expression)として値を返せる
  • catch で例外を補足し、エラーメッセージなどを取得可能
  • finally は例外発生に関わらず必ず実行

2. 例外階層:Throwable とその派生

Kotlin(およびJVM)では、例外は Throwable クラスを基底に持ちます。

クラス 意味
Error システム的な致命的エラー OutOfMemoryError
Exception 通常の実行時例外 IOException, RuntimeException
RuntimeException 実行中のロジックエラー NullPointerException
Checked Exception Javaのみ。Kotlinでは存在しない

Kotlin には Checked Exception(例外宣言強制) が存在しません。
→ より柔軟に例外を扱えるが、自分で責任を持ってハンドリングする必要があります。


3. runCatchingResult による安全な処理

Kotlin では、例外を値として扱える runCatching ブロックが推奨されています。

fun safeDivide(a: Int, b: Int): Result<Int> =
    runCatching { a / b }

fun main() {
    val result = safeDivide(10, 0)
    result.onSuccess { println("結果: $it") }
          .onFailure { println("失敗: ${it.message}") }
}

Result のチェーン利用

val data = runCatching { "100".toInt() }
    .map { it * 2 }
    .recover { 0 }
    .getOrThrow()

println(data) // => 200
メソッド 意味
map 成功時の値を変換
recover 失敗時に代替値を返す
getOrThrow 成功なら値、失敗なら例外再スロー
getOrNull 成功なら値、失敗なら null

特徴:

  • 例外を明示的に扱わずに、安全なチェーンが書ける
  • 関数型スタイルで扱いやすい(エラーハンドリングも表現的)

4. コルーチンでの例外処理

コルーチンでは例外が非同期的に発生するため、特別な仕組みが必要です。

try-catch は有効だが、launchとasyncで挙動が異なる

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            throw RuntimeException("Error in launch")
        } catch (e: Exception) {
            println("Caught in launch: ${e.message}")
        }
    }
    job.join()
}

launch例外を自動的に上位スコープへ伝播しますが、
async結果を待つ (await()) まで例外が保留されます。

val deferred = async {
    throw RuntimeException("Error in async")
}
try {
    deferred.await()
} catch (e: Exception) {
    println("Caught in async: ${e.message}")
}

CoroutineExceptionHandler

グローバルに例外を処理したい場合は、CoroutineExceptionHandler を使います。

val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught by handler: ${exception.message}")
}

fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob() + handler)

    scope.launch {
        throw RuntimeException("Something went wrong")
    }

    delay(100L)
}

ポイント:

  • CoroutineExceptionHandlerlaunch 系にのみ有効
  • async の場合は await() 時に手動で補足する必要あり
  • SupervisorJob を組み合わせると他の子コルーチンへ伝播しない

5. 実戦設計パターン:安全なエラーハンドリング構造

例外処理を Clean Architecture 的に整理する場合:

suspend fun fetchUser(id: String): Result<User> =
    runCatching {
        api.getUser(id).also {
            if (it.isBanned) throw IllegalStateException("User banned")
        }
    }

fun handleUserResult(result: Result<User>) {
    result.fold(
        onSuccess = { println("User: ${it.name}") },
        onFailure = { println("Error: ${it.message}") }
    )
}

メリット

  • 例外を「値」として上層に返せる(副作用を分離)
  • runCatching で try/catch 構文を簡潔化
  • コルーチンとも自然に統合できる

まとめ

構文 / API 特徴
try-catch-finally 基本構文。副作用処理にも使用可
throw / Throwable 例外階層の基本構造
runCatching 例外を安全に値として扱う
Result 成功 / 失敗を表すデータ型
CoroutineExceptionHandler 非同期例外のグローバル処理
SupervisorJob 子コルーチン間の独立性を確保

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?