プログラミングガチの初学者へ向けた例外処理について

- [はじめに](#はじめに)
- [この記事で伝えたいこと](#この記事で伝えたいこと)
- [例 1](#例-1)
  - [実はよくないコードを書いていたという話](#実はよくないコードを書いていたという話)
- [例 2](#例-2)
- [もう一度この記事で伝えたいこと](#もう一度この記事で伝えたいこと)



# はじめに

例外処理で大事なことを記述しました。  
catch 時の処理方法が抽象的になってしまったので理解しづらいかもしれません。 　
私自身、プログラミングの実務経験がないのでそういった解釈方法もあるのだな程度に気を留めて頂けると幸いです。  
プログラミング言語は Kotlin を用います。  
https://paiza.io/ja で言語を Kotlin にしていただければコピペでコードが動きます。  

# この記事で伝えたいこと

- 例外は起こるとわかっているものだけ処理する。  
- 例外処理時にメッセージを出す場合、誰に伝えたいのか考える。  

# 例 1

Kotlin では `throw ArithmeticException()` と記述すると故意に例外を起こせます。  
例外を投げるともいいます。  

```kt
fun main(args: Array<String>) {
    throw ArithmeticException()
    println("このコードは無視されます")
}
```

例外後のコードは無視され、出力には `Exception in thread "main" java.lang.ArithmeticException at MainKt.main(Main.kt:2)` と表示されます。  
例外に対して try catch といった例外処理をしないとアプリが強制終了されます。  

エラーを防ぐために `try {}` で例外が起こる部分を囲み、 `catch (e: Exception) {}` で例外が起こった場合に実行するコードを記述します。  
try {} で例外が起こると、それ以降の try {} 内部のコードは実行されません。  
`catch (e: Exception) {}` 部分が実行されます。  

```kt
fun main(args: Array<String>) {
    try {
        throw ArithmeticException()
        println("このコードは無視されます")
    } catch (e: Exception) {
        println(e)
    }
}
```

このコードを実行すると出力に `java.lang.ArithmeticException` と表示されます。  
これによって今回起こっている例外が、`java.lang.ArithmeticException` だとわかります。  

ArithmeticException で検索してみると算術計算で例外が発生すると起こるようです。  
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ArithmeticException.html  

catch 時の表示内容を変更してみます。  

```kt
fun main(args: Array<String>) {
    try {
        throw ArithmeticException()
    } catch (e: Exception) {
        println("ArithmeticException に対する処理をします")
    }
}
```

このコードを実行すると出力に `ArithmeticException に対する処理をします` と表示されます。  
catch 内部は println() のみなので文字列を表示するだけですが、実際には他にも複数のコードがあり処理をしていると思ってここからは読んでください。  


例外を起こす部分を `throw IllegalStateException()` と記述すると `例外の種類は変わります` が変更してみます。  
IllegalStateException は不適切な時にメソッドが呼び出されたことを示すようです。  
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/IllegalStateException.html  

```kt
fun main(args: Array<String>) {
    try {
        throw IllegalStateException()
    } catch (e: Exception) {
        println("ArithmeticException に対する処理をします")
    }
}
```

このコードを実行すると出力に `ArithmeticException に対する処理をします` と表示されます。  

`throw NullPointerException()` でも試してみます。  

```kt
fun main(args: Array<String>) {
    try {
        throw NullPointerException()
    } catch (e: Exception) {
        println("ArithmeticException に対する処理をします")
    }
}
```

このコードを実行すると出力に `ArithmeticException に対する処理をします` と表示されます。  
理由は `catch (e: Exception)` とかいてしまっているからです。  

## 実はよくないコードを書いていたという話

ここが良くない理由です。  
どのような例外が起こっても表示されるメッセージが同じになってしまいます。  
今回は例外時の処理が println() 関数だけなので println(e) と記述しておけば、別のメッセージにできます。  
しかし、実際は複数の例外に対して同じ処理をするわけではないと思うので、起こるとわかっている例外に対してのみ例外処理を行うようにします。  

catch 部分は `if else` の else のように複数記述できます。  
なので、具体的に例外の指定をします。  

```kt
fun main(args: Array<String>) {
    try {
        throw IllegalStateException()
    } catch (e: IllegalStateException) {
        println("IllegalStateException に対する処理をします")
    } catch (e: Exception) {
        println("ArithmeticException に対する処理をします")
    }
}
```

こうすれば、`IllegalStateException に対する処理をしました` と表示されるはずです。  
このように　catch も上から順番で当てはまった最初の catch 部分のみ実行します。  
先ほどまで実行されていた Exception 部分は実行されません。  

Exception を `ArithmeticException` に変更し、起こす例外を `throw ArithmeticException()` に変更します。  

```kt
fun main(args: Array<String>) {
    try {
        throw ArithmeticException()
    } catch (e: IllegalStateException) {
        println("IllegalStateException に対する処理をします")
    } catch (e: ArithmeticException) {
        println("ArithmeticException に対する処理をします")
    }
}
```

`ArithmeticException に対する処理をします` と表示されるはずです。  

では、起こす例外を `throw NullPointerException()` に変更します。  

```kt
fun main(args: Array<String>) {
    try {
        throw NullPointerException()
    } catch (e: IllegalStateException) {
        println("IllegalStateException に対する処理をします")
    } catch (e: ArithmeticException) {
        println("ArithmeticException に対する処理をします")
    }
}
```

`Exception in thread "main" java.lang.NullPointerException at MainKt.main(Main.kt:3)` と表示されるはずです。  

これは、`NullPointerException` に対する処理を記述していないからです。  

Exception で catch してしまうと、予期せぬ例外もそこで処理してしまうので自身が起こると把握している例外のみ catch 処理するようにしましょう。  

# 例 2

標準入力には 0 を受け取るとします。  
（paiza.io の入力に 0 と記述してください）  

```kt
fun main(args: Array<String>) {
    val a = readLine()!!.toInt()
    try {
        println(10/a)
    } catch (e: ArithmeticException) {
        println(e)
    }
}
```

`java.lang.ArithmeticException: / by zero` が出力されます。  
プログラムを書く人にとってはデバッグ時このように表示されると、エラーの理由がわかるので嬉しいと思います。  

ただ、アプリケーションを利用する人からすると『英語だし何いってるのかわからん』といった状況に陥りそうです。  
さらにアプリケーションのコードを変更できるわけではないので、アプリケーション利用時に表示されても困ります。  
よって表示する内容を変更します。  

```kt
fun main(args: Array<String>) {
    val a = readLine()!!.toInt()
    try {
        println(10/a)
    } catch (e: ArithmeticException) {
        println("0 で割ってはいけません")
    }
}
```

こうすると `0 で割ってはいけません` と出力され、先ほどの内容よりアプリユーザーに対して親切になったと思います。  

# もう一度この記事で伝えたいこと

- 例外は起こるとわかっているものだけ処理する。  
- 例外処理時にメッセージを出す場合、誰に伝えたいのか考える。