Kotlinで例外処理ってどうやるんだろう
Kotlinでtry catchの使い方を学びたい
Kotlinで例外処理をしようと思ってもtry catchの使い方がよくわからなかったり、そもそもチェック例外が強制されなかったりとJavaなどとは少し違って戸惑っている人はいませんか?
こんにちは!エンジニアのかいです。Kotlinをこれから始めようとしている人、もしくは始めたての人であればプログラミングの基本中の基本である例外処理、try catchについて上記のような悩みを抱えている方はいませんか?
そこで、本日はKotlin初心者の方を対象にtry catchの基本的な使い方や、基本的な例外処理の概念について説明していきます。
この記事はこんな人のために書きました。
- Kotlinでtry catchを使いこなしたい
- Kotlinの例外処理がよくわからない
try-catchの使い方をみてみよう
まずは、例外処理を行うためのtry catchの基本的な使い方を見ていきましょう。
サンプルプログラム1:
fun main() { var test = "testes" try { val testInt = test.toInt() println(testInt) }catch(e: Exception){ println(e) } }
java.lang.NumberFormatException: For input string: "testes"
サンプルプログラム2:
fun main() { var test = "3" try { val testInt = test.toInt() println(testInt) }catch(e: Exception){ println(e) } }
3
Javaや他言語でtry catchを書いてきた人には特に問題なく理解いただけると思いますが、ほとんど書き方は同じですね。エラーを返す可能性がある処理をtryでくくって、そのエラーをcatchで捕捉してやればよいです。
今回はExceptionで全ての例外を捕捉していますが、これをNumberFormatExceptionなどエラーを指定して捉えることももちろん可能です。
finallyの使い方
さて、次はfinallyを使ってみましょう。finallyとは、エラー捕捉の有無にかかわらず、tryの最後に必ず実行されるものです。
つまり、catchされたにしてもされないにしても必ず実行されます。以下例をみてみましょう。
サンプルプログラム1:
import java.lang.NumberFormatException fun main() { // エラーが捕捉される var test = "testes" try { val testInt = test.toInt() println(testInt) }catch(e: NumberFormatException){ println(e) }finally { println("done") } }
java.lang.NumberFormatException: For input string: "testes" done
サンプルプログラム2:
import java.lang.NumberFormatException fun main() { // エラーが捕捉されない var test = "3" try { val testInt = test.toInt() println(testInt) }catch(e: NumberFormatException){ println(e) }finally { println("done") } }
3 done
このように、finallyは必ず最後に実行されていますね。
try-catchを複数使ってみる
次は、try catchをネストさせて書いてみましょう。
サンプルプログラム1:
import java.lang.Exception import java.lang.NumberFormatException fun main() { var test = "3" val test2 = try { test.toInt() }catch(e: NumberFormatException){ try{ test.toByteArray() }catch(e: Exception){ null } } println(test2) }
3
サンプルプログラム2:
import java.lang.Exception import java.lang.NumberFormatException fun main() { var test = "testes" val test2 = try { test.toInt() }catch(e: NumberFormatException){ try{ test.toByteArray() }catch(e: Exception){ null } } println(test2) }
[B@7229724f
このようにcatchの中にtryをまた書いてやることでtryをネストさせることができます。ただ、可読性が下がるのでできるだけ避けたほうが無難です。
複数のエラーを捕捉する場合
ちなみに複数のエラーが出る可能性があってエラーによって処理を変えたい場合は以下のようにかけます。
サンプルプログラム1:
import java.lang.Exception import java.lang.NumberFormatException fun main() { var test = "testes" val test2 = try { test.toInt() }catch(e: Exception){ when(e){ is NumberFormatException -> println("Numerr!") else -> println("else!") } } }
Numerr!
サンプルプログラム2:
import java.lang.Exception import java.lang.NumberFormatException fun main() { var test = arrayListOf(1, 2) val test2 = try { test[3] }catch(e: Exception){ when(e){ is NumberFormatException -> println("Numerr!") else -> println("else!") } } }
else!
このようにwhenをつかってエラーによって条件分岐してやればよいですね。whenの使い方については以下の記事に書いてあるので参考にしてみてください。
Kotlinでtryを使う時の注意点
さて、ここでKotlinの例外処理が他と違う点を述べていこうと思います。例外処理が違う点というよりは、Javaでは言語の仕様として検査例外と非検査例外というものがありましたが、Kotlinには検査例外という仕組みはありません。
例外の処理について触れておくと、検査例外とはチェック例外などとも言われ、簡単にいうとコードではなく外部に依存しているエラーのことです。例えば、ディスクの容量が足りなかったり、ファイルが壊れていたりする場合にその処理に該当するコードで発生するエラーですね。
JavaのException以下でRuntime Exception以外のクラスなどとも言われますね。Javaではこの検査例外はエラーの捕捉が強制されており、例外処理を行わないとコンパイルが通りません。
そして、非検査例外はRuntime Exception以下のクラスのことでこれはコードレベルで問題がある場合のエラーですね。NullPointerExceptionやArrayIndexOutOfBoundsExceptionが馴染み深いのではないでしょうか。
例外についてわかったところでもう一度Kotlinに戻ると、先ほど述べたようにKotlinには検査例外というものがないのでJavaのようにtry catchを強制させられることはありません。
実はKotlinのみならずモダンな言語では、検査例外がないという仕様を担っているものが多いです。その理由としては、非同期処理との相性の悪さや、try catchでエラーが捕捉しきれなかったり、エラーが握りつぶされたりするからですね。
import java.lang.Exception fun addNum(num1: Int, num2: Int): Int{ val res = num1 + num2 if(res > 0){ return res }else{ throw Exception("0より小さい値は返せません") } } fun main() { val result = addNum(-1, 0) println(result) }
Exception in thread "main" java.lang.Exception: 0より小さい値は返せません at TestKt.addNum(test.kt:9) at TestKt.main(test.kt:14) at TestKt.main(test.kt)
例えばこのように例外をthrowしてもtry catchを強制されることはありません。とはいえもちろん捕捉することはできます。
まとめ
本日はKotlinの例外処理について話してきました。KotlinにはResult型などでエラー処理を行うことも可能でtry catchばかりを使っていると適切でない場面もあるので場合に応じて使い分けていきましょう!
それでは!!