読み込み中...

KotlinのrunCatchingの完全ガイド!10選の詳細サンプルコードで理解しよう!

KotlinのrunCatching関数のロゴとサンプルコードのスクリーンショット Kotlin
この記事は約20分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Kotlinを始めたばかりの方や、Kotlinでのエラーハンドリングに悩んでいる方、必見です!

この記事を読めば、KotlinのrunCatching関数の使い方を習得できます。

runCatchingはKotlinでのエラーハンドリングを効率的に行うための関数で、この関数を理解することで、コードの安定性や可読性が大幅に向上します。

エラーハンドリングはプログラムを書く上で避けて通れない道ですが、runCatchingを活用することで、その複雑さを一気に簡素化できます。

この記事では、その魅力的なrunCatching関数の基本的な使い方から、さまざまな応用例、注意点、カスタマイズ方法までを、初心者にもわかるように詳しく解説します。

●KotlinのrunCatchingとは

runCatchingはKotlinの標準ライブラリに含まれる関数の一つで、エラーハンドリングをよりシンプルに、そして安全に行うことをサポートしています。

○runCatching関数の概要

runCatching関数は、特定の処理を実行し、その処理が正常に終了した場合は結果を、エラーが発生した場合はエラー情報を取得するための関数です。

この関数を利用することで、エラーが発生する可能性がある処理を安全に実行し、その結果を簡単に扱うことができます。

○runCatchingの基本的な役割

Kotlinでは、エラーを投げることができる関数やメソッドは特別に宣言する必要はありません。

しかし、エラーが発生する可能性のある処理を行う場合、そのエラーを適切に捕捉して処理する必要があります。

そこで登場するのがrunCatching関数です。

この関数の役割は大きく分けて2つです。

  1. エラーが発生する可能性のある処理を安全に実行する。
  2. 実行結果またはエラー情報を返す。

runCatchingを利用することで、通常のtry-catch文を用いたエラーハンドリングよりも、シンプルで読みやすいコードを実現することができます。

●runCatchingの使い方

KotlinのrunCatching関数を実際にどのように活用するのか、具体的な使い方から探っていきましょう。

初心者の方でもスムーズに取り入れることができるよう、具体的なサンプルコードを用いて詳しく解説します。

○サンプルコード1:基本的なrunCatchingの使用例

Kotlinでエラーが発生する可能性のある処理を安全に実行する際の基本的なパターンです。

fun main() {
    val result = runCatching {
        // エラーが発生する可能性のある処理
        10 / 2
    }

    result.onSuccess { 
        println("成功時の結果: $it") 
    }.onFailure { 
        println("エラー発生: ${it.message}") 
    }
}

このコードではrunCatchingを使って、10を2で割る処理を行っています。

エラーが発生しない場合、onSuccess内の処理が実行され、結果が表示されます。

エラーが発生した場合、onFailure内の処理が実行され、エラーメッセージが表示されます。

このコードを実行すると、次のような出力結果が得られます。

成功時の結果: 5

○サンプルコード2:エラーハンドリング時の処理

次に、エラーが実際に発生する場面のrunCatchingの使用例を見てみましょう。

fun main() {
    val result = runCatching {
        // エラーが発生する処理
        10 / 0
    }

    result.onSuccess { 
        println("成功時の結果: $it") 
    }.onFailure { 
        println("エラー発生: ${it.message}") 
    }
}

このコードでは、10を0で割るという、エラーが発生する処理をrunCatching内に書いています。

この場合、onSuccessのブロックはスキップされ、onFailureのブロックが実行されます。

このコードを実行すると、次のような出力結果が得られます。

エラー発生: / by zero

○サンプルコード3:結果の取得と利用方法

runCatching関数の利点の1つは、成功時や失敗時の結果を簡単に取得できる点です。

結果はResult型として取得され、getOrNull()exceptionOrNull()を用いることでその結果を取り出すことができます。

fun main() {
    val divisionResult = runCatching {
        20 / 2
    }

    val resultValue = divisionResult.getOrNull()
    val exceptionValue = divisionResult.exceptionOrNull()

    if (resultValue != null) {
        println("計算結果は $resultValue です。")
    } else {
        println("エラーが発生しました: ${exceptionValue?.message}")
    }
}

このコードでは20を2で割る計算をrunCatchingで実行しています。

エラーが発生しない場合、getOrNull()を使用して計算結果を取得します。

エラーが発生した場合、exceptionOrNull()でエラー情報を取得することができます。

上記のコードを実行すると、次の出力が得られます。

計算結果は 10 です。

○サンプルコード4:runCatchingと他の関数との組み合わせ

Kotlinの強力な関数型機能を利用して、runCatchingを他の関数と組み合わせることもできます。

例として、map関数を使って、runCatchingの結果を変換する方法を見てみましょう。

fun main() {
    val stringLengthResult = runCatching {
        "Kotlin".length
    }.map {
        it * 2
    }

    println("文字列の長さの2倍は ${stringLengthResult.getOrNull()} です。")
}

このコードでは、文字列”Kotlin”の長さを取得し、その後map関数を用いてその2倍の値を計算しています。

上記のコードを実行すると、次の結果が出力されます。

文字列の長さの2倍は 12 です。

●runCatchingの応用例

KotlinのrunCatching関数は、エラーハンドリングのみならず、さまざまな応用例が考えられます。

ここでは、その応用例を詳しく解説し、サンプルコードを交えて実際の使用方法を見ていきます。

○サンプルコード5:複数の処理を連鎖させる場合

runCatching関数は、他の関数と連鎖して使うことができます。

これにより、複数の処理を順番に実行し、途中でエラーが発生した場合にも適切にハンドリングすることができます。

fun divideByTwo(n: Int) = n / 2
fun multiplyByThree(n: Int) = n * 3

fun main() {
    val result = runCatching { divideByTwo(10) }
        .map { multiplyByThree(it) }

    if (result.isSuccess) {
        println("最終結果は ${result.getOrNull()} です。")
    } else {
        println("処理中にエラーが発生しました。")
    }
}

このコードでは、まず10を2で割る処理を行い、その結果を3倍する処理を連鎖しています。

全ての処理が成功すれば、最終結果を出力します。

上記のコードを実行すると、次のような結果が表示されます。

最終結果は 15 です。

○サンプルコード6:外部リソースの取得時のエラーハンドリング

外部リソースの取得は、ネットワークエラーなど様々な問題が考えられるため、エラーハンドリングが非常に重要です。

runCatchingを使用することで、外部リソースの取得時のエラーもスムーズにハンドリングすることができます。

fun fetchExternalResource(url: String): String {
    // この部分は外部リソースを取得するダミーコードです。
    if (url == "https://error.url") {
        throw Exception("取得エラーが発生しました。")
    }
    return "リソースの内容"
}

fun main() {
    val url = "https://error.url"
    val result = runCatching { fetchExternalResource(url) }

    if (result.isSuccess) {
        println("取得したリソース: ${result.getOrNull()}")
    } else {
        println("エラーが発生しました: ${result.exceptionOrNull()?.message}")
    }
}

このコードでは、外部リソースの取得を模倣したダミー関数を使用しています。

特定のURLでエラーをシミュレートしており、そのエラーをrunCatchingでキャッチしています。

上記のコードを実行すると、次のような結果が得られます。

エラーが発生しました: 取得エラーが発生しました。

○サンプルコード7:非同期処理と組み合わせた場合

Kotlinでは非同期処理を行う際に、coroutinesを使用するのが一般的です。

このcoroutinesを用いた非同期処理中にエラーが発生する場合、runCatching関数でエラーハンドリングを行うことが可能です。

この組み合わせによって、非同期処理の安全性をさらに向上させることができます。

例として、非同期に外部サーバからデータを取得する関数を考えます。

この関数は、通常の場合にはデータを返すが、何らかの理由でデータの取得に失敗する場合もあります。

import kotlinx.coroutines.*

suspend fun fetchDataFromServer(): String {
    delay(1000) // 1秒後にデータを取得すると仮定
    // 50%の確率でエラーを発生させる
    if (Math.random() > 0.5) {
        throw Exception("データ取得中のエラー")
    }
    return "サーバからのデータ"
}

fun main() = runBlocking {
    val result = runCatching { fetchDataFromServer() }
    if (result.isSuccess) {
        println("取得したデータ: ${result.getOrNull()}")
    } else {
        println("非同期処理中のエラー: ${result.exceptionOrNull()?.message}")
    }
}

上記のコードでは、fetchDataFromServer関数は非同期に動作し、1秒後にデータを取得する処理を模倣しています。

そして、ランダムにエラーを発生させています。

このエラーは、runCatching関数で捕捉され、エラーメッセージが出力されます。

上記のコードを実行すると、次のような結果のいずれかが表示されます。

取得したデータ: サーバからのデータ

または

非同期処理中のエラー: データ取得中のエラー

○サンプルコード8:特定のエラータイプへの対応

エラーハンドリングを行う際、特定のエラータイプにのみ反応して処理を行いたい場合があります。

runCatching関数とKotlinのwhen式を組み合わせることで、このような要件にも対応することができます。

例として、数値の除算を行う関数を考えます。

この関数は、0での除算や数値変換のエラーを投げる可能性があります。

fun divideNumbers(numerator: String, denominator: String): Double {
    val num = numerator.toDouble()
    val den = denominator.toDouble()
    if (den == 0.0) {
        throw ArithmeticException("0での除算エラー")
    }
    return num / den
}

fun main() {
    val result = runCatching { divideNumbers("10", "0") }
    when (val exception = result.exceptionOrNull()) {
        is ArithmeticException -> println("算数エラーが発生: ${exception.message}")
        is NumberFormatException -> println("数値変換エラーが発生")
        else -> {
            if (result.isSuccess) {
                println("計算結果: ${result.getOrNull()}")
            } else {
                println("未知のエラーが発生")
            }
        }
    }
}

このコードを実行すると、次のような結果が表示されます。

算数エラーが発生: 0での除算エラー

○サンプルコード9:カスタムエラーメッセージの利用

KotlinのrunCatchingを使用する際、エラーメッセージをカスタマイズして特定のエラーシチュエーションに対応することが可能です。

カスタムエラーメッセージを利用することで、開発者や利用者にとってわかりやすく具体的なエラー情報を提供することができます。

例として、数値の変換処理を行う関数を考えます。

この関数は、文字列が数値に変換できない場合、特定のカスタムエラーメッセージを返すようにしたいと思います。

fun convertStringToDouble(input: String): Double {
    return try {
        input.toDouble()
    } catch (e: NumberFormatException) {
        throw IllegalArgumentException("「$input」は数値に変換できません。正しい数値形式の文字列を入力してください。")
    }
}

fun main() {
    val result = runCatching { convertStringToDouble("abc") }
    val output = result.fold(
        onSuccess = { "変換成功: $it" },
        onFailure = { it.message ?: "未知のエラーが発生しました" }
    )
    println(output)
}

このコードではconvertStringToDouble関数内で、数値変換が失敗した際にIllegalArgumentExceptionをスローし、具体的なエラーメッセージを設定しています。

そして、runCatchingを使用してエラーハンドリングを行い、成功時と失敗時でそれぞれの結果を出力します。

このコードを実行すると、次のメッセージが表示されます。

「abc」は数値に変換できません。正しい数値形式の文字列を入力してください。

このように、エラー発生時の状況や内容に合わせて、詳細で具体的なカスタムエラーメッセージを用意することで、問題の特定や対応が容易になります。

○サンプルコード10:特定の条件下でのエラーハンドリング

runCatching関数を利用する上で、特定の条件下でのみエラーハンドリングを行いたい場合も考えられます。

例えば、外部APIからのレスポンスが期待した形式でない場合に、エラーを発生させたいとします。

data class ApiResponse(val data: String?, val errorCode: Int?)

fun handleApiResponse(response: ApiResponse) {
    if (response.data == null || response.errorCode != null) {
        throw IllegalStateException("APIレスポンスが期待した形式ではありません。errorCode: ${response.errorCode}")
    }
    println("データの取得に成功しました: ${response.data}")
}

fun main() {
    val response = ApiResponse(data = null, errorCode = 404)
    val result = runCatching { handleApiResponse(response) }
    val output = result.fold(
        onSuccess = { "APIからのデータ取得成功: $it" },
        onFailure = { "エラーが発生しました: ${it.message}" }
    )
    println(output)
}

このコードでは、handleApiResponse関数内でAPIのレスポンスが期待した形式でない場合にエラーをスローしています。

このエラーは、runCatching関数でキャッチされ、エラーメッセージが出力されます。

このコードを実行すると、次のメッセージが表示されます。

エラーが発生しました: APIレスポンスが期待した形式ではありません。errorCode: 404

こちらも前述のカスタムエラーメッセージの利用と同様に、特定の条件下でのエラーハンドリングを行うことで、問題の特定や対応が容易になります。

●注意点と対処法

runCatching関数はKotlinにおいてエラーハンドリングを簡潔に行うための非常に便利な関数です。

しかし、正しく使用しないと予期しない結果やエラーに繋がる可能性があります。

ここでは、runCatching関数を使用する際の一般的なエラーとその対処法、さらにエラーハンドリングのベストプラクティスについて解説します。

○runCatchingの際の一般的なエラー

runCatching関数を使用する際にはいくつかの一般的なエラーや落とし穴が存在します。

その中でも特によく遭遇するエラーについて、サンプルコードと共に詳しく見ていきます。

□戻り値の取得の際のエラー

runCatching関数の戻り値はResult型となります。

このResult型から値を取得する際に、不適切な方法でアクセスするとエラーが発生します。

// 例: 不適切な値の取得方法
val result = runCatching { 10 / 2 }
val value = result.getOrNull()!!
println(value)

このコードは、getOrNull関数の戻り値(Nullable型)に対して!!を使用して非Nullを強制しています。

しかし、エラーが発生した場合、Nullポインターエクセプションがスローされるリスクが高まります。

○エラーハンドリング時のベストプラクティス

エラーハンドリングはアプリケーションの安定性や信頼性を保つ上で非常に重要です。

runCatchingを使用する際のベストプラクティスを紹介します。

□戻り値の適切な取得方法

Result型の戻り値から値を安全に取得するためには、getOrNullgetOrElseなどの関数を使用するのがベストです。

val result = runCatching { 10 / 2 }
val value = result.getOrElse { -1 }  // エラー時には-1を返す
println(value)

このコードでは、getOrElse関数を使用して、エラーが発生した場合に-1を返すようにしています。

□具体的なエラーメッセージの利用

エラーメッセージはトラブルシューティングの際に非常に役立ちます。

runCatching内でエラーが発生した場合、具体的なエラーメッセージを提供するようにしましょう。

□外部ライブラリやAPIのエラーに備える

外部ライブラリやAPIを使用する際、そのライブラリやAPIのエラーに備えて、適切なエラーハンドリングを行う必要があります。

事前にドキュメントを参照し、エラーの種類や原因を理解しておくと、エラーハンドリングが容易になります。

●カスタマイズ方法

KotlinのrunCatching関数は、その基本的な機能だけでなく、カスタマイズすることでさまざまなニーズに合わせて利用することができます。

ここでは、runCatching関数のカスタマイズ方法を2つの観点から深堀りしていきます。

○カスタムエラーメッセージの設定方法

エラーメッセージはデバッグやトラブルシューティングの際に非常に役立ちます。

デフォルトのエラーメッセージだけでなく、独自のエラーメッセージを設定することができます。

サンプルコードを見てみましょう。

val result = runCatching {
    val value = 10 / 0
    value
}.onFailure {
    throw IllegalArgumentException("数値の計算中にエラーが発生しました:${it.message}")
}

println(result.exceptionOrNull()?.message)

このコードでは、0での除算エラーが発生する場所で、onFailure関数を用いて独自のエラーメッセージを設定しています。

このコードを実行すると、独自のエラーメッセージ「数値の計算中にエラーが発生しました:/ by zero」が出力されることになります。

○runCatching関数の拡張方法

runCatching関数はその自体が非常に柔軟であり、さらなる機能を追加するための拡張も可能です。

例えば、特定のエラータイプに対してログを出力したり、リトライ機能を追加したりすることが考えられます。

ここでは、リトライ機能を拡張したサンプルコードを紹介します。

fun <T> runCatchingWithRetry(times: Int, action: () -> T): Result<T> {
    var lastException: Exception? = null
    repeat(times) {
        runCatching {
            action()
        }.onSuccess {
            return it
        }.onFailure {
            lastException = it
        }
    }
    throw lastException ?: Exception("未知のエラー")
}

val result = runCatchingWithRetry(3) {
    // 何らかのリトライが必要な処理
    // 今回はダミーとして除算エラーを発生させる
    10 / 0
}

println(result.exceptionOrNull()?.message)

このコードでは、runCatchingWithRetryという新しい関数を定義し、引数で指定した回数だけリトライを試みます。

リトライ中に成功すればその結果を返し、全てのリトライが失敗した場合は最後のエラーをスローします。

このコードを実行すると、「/ by zero」というエラーメッセージが3回のリトライ後に出力されることになります。

まとめ

KotlinのrunCatching関数は、エラーハンドリングをシンプルかつ効率的に行うための強力なツールです。

この記事を通して、その基本的な使い方から、エラーハンドリングの方法、応用例、注意点、さらにはカスタマイズ方法まで幅広く解説してきました。

エラーハンドリングはプログラムの品質を高め、ユーザー体験を向上させる上で欠かせない要素です。

runCatching関数を活用することで、エラー処理を明確にし、コードの可読性やメンテナンス性を向上させることができます。

また、カスタマイズの方法を取り入れることで、runCatching関数を独自のニーズに合わせて更に強化することができます。

これにより、より複雑なシナリオや特定の条件下でのエラーハンドリングも柔軟に対応することが可能となります。

Kotlin初心者から経験者まで、runCatching関数を最大限に活用し、安定性と品質の高いプログラムを作成するための知識とノウハウを習得することができたことでしょう。

プログラムの中で起こるさまざまなエラーに対応する際、この記事が皆さまの頼りになる参考資料として役立つことを願っています。