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入門|コルーチンでの例外処理の基本と実践例

Posted at

コルーチンでの例外処理の基礎

こんにちは、今回はコルーチン内で生じる例外処理の基礎についてまとめました。

コルーチンとは?に関しては公式ドキュメントを参照して、以前まとめているのでこちらを参照ください。

また、今回も公式ドキュメントを参考に学習した内容なので、詳しくは以下のURLで学習してみてください。

例外の概要

例外とは、コードの実行中に発生する予期しないイベントです。

fun main() {
    val x = 0
    val y = 2
    println(y / x) //0で割ることはできない
}

このプログラムを実行すると、値をゼロで割ることはできないため、プログラムは計算例外でクラッシュします。

コルーチン上での例外

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        println(getWeatherReport())
        println("Have a good day!")
    }
}

suspend fun getWeatherReport() = coroutineScope { //親コルーチン
    val forecast = async { getForecast() }
    val temperature = async { getTemperature() }
    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String { //子コルーチン
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String { //子コルーチン
    delay(500)
    throw AssertionError("Temperature is invalid")//エラーを意図的に発生させる
    return "30\u00b0C"
}

このプログラムは、天気と気温を出力するコルーチンサンプルプログラムですが、getTemperature()で意図的にエラーを投げてみます。

  • throw:意図的にエラーを投げるキーワード
Weather forecast
Exception in thread "main" java.lang.AssertionError: Temperature is invalid
 at FileKt.getTemperature (File.kt:24) 
 at FileKt$getTemperature$1.invokeSuspend (File.kt:-1) 
 at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) 

出力結果は上記のようになります。
何が起きているのかというと、親と子の関係を把握することでイメージができます。
ここでいう親とはgetWeatherReport()であり、中にあるgetForecast()getTemperature()が子に当たります。
子コルーチンのどちらか一つでも例外を吐いてしまうと親コルーチン全体で処理が止まってしまうのです。

プログラムが複雑になるにつれて、例外は必然的に出てくるので、毎回止まってしまっては話になりません。
なので、例外処理をする必要があります。

try-catchによる例外処理

  • try-catch:エラーが起こる可能性のある処理を安全に実行し、例外発生時に適切に対処するための構文です。
try {
    // エラーが起きうるプログラム
} catch (e: IllegalArgumentException) {
    // そこでエラーが生じたときの適切な処理
}

try-catchを用いて先ほど生じたエラーの対処方法を2つ紹介します。
main()関数内のprintln(getWeatherReport())で例外処理をする
② suspend関数getWeatherReport()内のasync { getTemperature() }に例外処理をする

順番に説明していきます。


try-catch①

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        try {
            println(getWeatherReport()) //気温を出力時にエラーが発生するはず
        } catch (e: AssertionError) {
            println("Caught exception in runBlocking(): $e")
            println("Report unavailable at this time")
        }
        println("Have a good day!")
    }
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() }
    val temperature = async { getTemperature() }
    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(500)
    throw AssertionError("Temperature is invalid") //意図的にエラーを発生
    return "30\u00b0C"
}
  • e:発生した例外オブジェクト(エラー情報)を受け取るための変数(eは慣例で使われるが、任意の変数名でOK)

この対処法は比較的わかりやすいと思います。main()内で明らかにprintln(getWeatherReport())でエラーが発生することは直感的にわかると思います。そこにtry-catchで囲い、生じたエラー情報を変数eに代入(このときの型は AssertionError クラス)し、どういったエラーが発生したかを表示する処理をします。

Weather forecast
Caught exception in runBlocking(): java.lang.AssertionError: Temperature is invalid
Report unavailable at this time
Have a good day!

例外処理により、親コルーチンすべてが止まることなく、Have a good day!まで出力ができています。


try-catch②

次は①よりも本質に迫った例外処理になります。
そもそも何が原因でエラーになっているのかを考えると、getTemperatureでエラーが発生しています。
つまり、getForecast()には問題はなく、getWeatherReport()全体に例外処理をする必要がないということです。

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        println(getWeatherReport())
        println("Have a good day!")
    }
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() } //問題なし
    val temperature = async { //ここにだけ例外処理を加える
        try {
            getTemperature()
        } catch (e: AssertionError) {
            println("Caught exception $e")
            "{ No temperature found }"
        }
    }

    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(500)
    throw AssertionError("Temperature is invalid") //エラーを意図的に発生
    return "30\u00b0C"
}

このプログラムを実行すると、

Weather forecast
Caught exception java.lang.AssertionError: Temperature is invalid
Sunny { No temperature found } //Sunnyは問題なく出力される
Have a good day!

この様にSunnyだけは無事に出力され、気温の出力のみ例外処理が行われています。

最後に

今回は公式ドキュメントのコードを引用し、try-catchの基礎を学習しました。

  • コルーチンでは、子の例外が親に伝播することを理解するのが重要。
  • try-catch はエラー処理の基本だが、使いどころを見極めると柔軟な設計ができる。

今後は、コルーチンコンセプトについて学んでいきたいと思います。

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?