覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Kotlin - 例外処理

「Kotlinイン・アクション」 を読みながらの写経まだまだ続いております。(Javaと比較しながら)

Kotlinイン・アクション

Kotlinイン・アクション

  • 作者: Dmitry Jemerov,Svetlana Isakova,長澤太郎,藤原聖,山本純平,yy_yank
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2017/10/31
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (2件) を見る


Kotlin の例外処理は Java のそれとよく似ています。

関数は、関数に記述された処理を何事もなく正常に完了するか、異常が起こった場合に例外をスローする事ができます。
関数の呼び出し元で、その例外をキャッチして処理する事ができます。その例外はスタックのより上位にスローすることもできます。

例えば、値が 0〜100の範囲に収まっていない場合は、IllegalArgumentExceptionをスローする関数は以下のように記述できます。

fun validation(percentage :Int) {
    if (percentage !in 0..100) {
        throw IllegalArgumentException("A percentage must be between 0 and 100 [parameter: $percentage]")
    }
}

Javaと違って、例外のインスタンスを生成するのに new キーワードは不要となっています。

一応以下のようなコードで実行してみると

package net.yyuki.sandbox.exception

import java.lang.IllegalArgumentException


fun validation(percentage :Int) {
    if (percentage !in 0..100) {
        throw IllegalArgumentException("A percentage must be between 0 and 100 [parameter: $percentage]")
    }
}

fun main() {
    validation(101)
}

当然のように例外がスローされました

Exception in thread "main" java.lang.IllegalArgumentException: A percentage must be between 0 and 100 [parameter: 101]
    at net.yyuki.sandbox.exception.ExceptionTrialKt.validation(ExceptionTrial.kt:8)
    at net.yyuki.sandbox.exception.ExceptionTrialKt.main(ExceptionTrial.kt:14)
    at net.yyuki.sandbox.exception.ExceptionTrialKt.main(ExceptionTrial.kt)


try

Javaと違って、Kotlinでは throw構文は式となっているため、他の式の一部として利用する事ができます。

以下は、if の条件に当てはまる場合はその値を返し、そうでなければ例外がスローされます

fun validation(value :Int) =
    if (value in 0..100) {
        value
    } else {
        throw IllegalArgumentException("A percentage must be between 0 and 100 [parameter: $value]")
    }


try-catch-finally

Javaと同じく、例外を処理するためにcatch節, finally節 を伴ったtry構文を使用します。

以下は、ファイル操作(BufferedReader経由でのデータの読み取り)の処理を行う関数の例です

fun readLineCount(reader: BufferedReader): Int {
    try {
        val str = reader.readLine()    // <- IOException の可能性がある
        return str.toInt()
    } catch (e: NumberFormatException) {
        return 0
    } finally {
        reader.close()
    }
}

Java と違って throws 節が存在しません。
Javaでは、IOException は検査例外(checked exception)であるため、明示的な記述が必要です。
また、その関数の呼び出し側でも、検査例外を処理するか再スローするかといったコードの記述が必要となります。

が、他のモダンなJVM言語と同様に、Kotlin では検査例外と非検査例外を区別していません。
関数からスローされる例外を指定する必要はなく、利用側ではどの例外について処理してもいいし、しなくてもいいということになっています。

Java では検査例外の扱いに対するルールのために、例外を再スローしたり無視したりするための意味のないコードだらけになってしまいます。挙句に、実際に起こり得るエラーからは実装者を守ることになっていないことが経験的に分かっています。
そのため、Kotlinではこのような方針になっています。


式としてのtry

以下の関数を定義して実行してみます

fun printReadLineCount(reader: BufferedReader) {
    val number = try {
        Integer.parseInt(reader.readLine())
    } catch (e: NumberFormatException) {
        return
    }

    println("read value = $number")
}

この関数を以下で実行すると何も出力されません

val reader = BufferedReader(StringReader("not a number")
printReadLineCount(reader))

Kotlin の try キーワードは、if や when と同じように式として使用され、変数にその値を割り当てる事ができます。

この関数は、catch ブロックにおける return 文で処理が終わってしまい、それ以降の処理は実行されません。


関数を続行したい場合は、以下の様に catch 節でも値を持つ必要があります

fun printReadLineCount(reader: BufferedReader) {
    val number = try {
        Integer.parseInt(reader.readLine())
    } catch (e: NumberFormatException) {
        0
    }

    println("read value = $number")
}

この関数を同様に呼び出すと

val reader = BufferedReader(StringReader("not a number")
printReadLineCount(reader))

結果は以下のようになります

read value = 0