19.0414

Exception(実行時例外: コンパイルエラー は、パスしたが...)

実行事例外とは、
  コンパイル時には、エラーは、検知されませんでした。
  でも、実際に、プログラムを、実行してみると、
  落ちてしまいました(エラーが発生してしまいました)。
というものです。
以前、少し触れましたが、ここでは、
  実行時例外、対策
と、
  実行事例外 の、発生個所 を、突き止める
ことを、中心に見ていきましょう。


1. 例外 の、発生個所 を、突き止める

例外の発生個所を突き止めるために、例外が、発生するコードを、
見てみましょう。

まず、コードが、何をやっているのか、見てみましょう。
この、TestArrayE.kt ファイルには、
  2つの関数
が、あります。
  main()関数
  returnIntValue()関数
    / Int型の値を返す関数
3行目:
  Int型配列インスタンス/IntArrayインスタンス の、iArray を、定義しています
4行目:
    println(returnIntValue(iArray))
  の意味は、以下と同じです。
    val i: Int = returnIntValue(iArray)
    println(i)
  何しろ、println() の、引数は、Any?/なんでもOK なので、
  値を返す関数 であれば、そのまま入れても、OKなのです。
7,8行目:
  この関数は、
    IntArrayインスタンス を、受け取って(引数として)、
    その、IntArrayインスタンス の、インデックス番号 が、3 / [3]
    の、Int値 を、返す
  という、関数です。

それでは、まず、どうして、
  例外、ArrayIndexOutOfBoundsException
が、起こったのか?、を、考えてみましょう。
右のコードの、下の部分の、IntelliJ の、コンソール画面 を、見てみましょう。
 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: なになに
 at TestArrayEKt.returnIntValue(TestArrayE.kt:8) 
 at TestArrayEKt.main(TestArrayE.kt:4) 
 at TestArrayEKt.main(TestArrayE.kt) 
TryKotlin でも、多少違いますが、大体同じような、スタックトレース(と、呼びます)が、
出ます。
この見方は、一番下から見ていきます。
では、順番に見ていきましょう。

at TestArrayEKt.main(TestArrayE.kt) 
  at というのは、地点 を、表しています。
  ですから、まず、最初は、
    TestArrayE.kt の、main()関数 で、発生しました、、となります。
at TestArrayEKt.main(TestArrayE.kt:4) 
  以後、根本原因に向けて、追跡していきます。
  TestArrayE.kt の、main()関数 の、4行目 だと、言っています。
at TestArrayEKt.returnIntValue(TestArrayE.kt:8) 
  最後にたどり着いたのは、
  TestArrayE.kt の、returnIntValue()関数の部分の、
  TestArrayE.kt の、8行目 だと、言っていますね。
また、青く下線が引かれているところは、クリックすると、その行へ、リンクできます。

では、その、8行目 を、調べてみましょう。
分かりましたでしょうか?
そもそもの原因は、
  要素のない、配列の、インデックス に、アクセスしようとしている
ことです。
iArrayインスタンスの、インデックスは、 0, 1, 2 しかないのに、
  iArray[3]
という事を、実行しようとしています。
それで、
  ArrayIndexOutOfBoundsException
が、発生したのです。


2. ArrayIndexOutOfBoundsException 対策、をする

ArrayIndex...Exception 対策をしたコードは、こんなです。

try ~ catch
例外が、発生しそうだ!と、分かっている/かもしれないと思っている 場合には、
  try{ // コード } catch(e: Exception) { // コード }
こんなコードを、書きます。
イメージコードだと、
  やってみる {
      // 例外が発生しそうなコード
      // 本当に、例外 が、発生したら、例外オブジェクトを、投げます。
  } 例外が起きたら例外オブジェクトを、キャッチする(例外オブジェクト: その例外オブジェクトの型) {
      // 例外が起きた時に書くべきコード
  }

ここで、例外オブジェクト / 例外インスタンス が、登場しますが、
これは、
   例外が、発生した時に、try{ }ブロック で作られ、
   catch の 引数 として、キャッチされます。     

では、これを踏まえて、例外対策をしたコード を、見てみましょう。

iArray というのは、要素(1, 2, 3) で、
  インデックス が、[0]、[1]、[2]
とある、IntArrayインスタンス です。
また、
  returnIntValueUsingIndex()関数
は、引数として、v と index の、2つを持つ、関数 ですが、
  ,
で、区切ること は、覚えるべきですが、
考え方自体は、1つの時と同じだと思ってください。
この、returnIntValueUsingIndex()関数 は、
  配列インスタンス、Int型数値
を、引数として、受け取り、
  Int型数値 を、インデックスナンバー として使って、
  受け取った配列の要素 を、返す、関数
  (return/返す IntValue/Int値 UsingIndex/インデックスを使って)
です。

問題の個所は、
  println(returnIntValueUsingIndex(iArray, 3))
ですね。
ここで、returnIntValueUsingIndex()関数 を、使って、
  iArray の、インデックスナンバー 3 の、要素 を、ゲット/取得 しようとしています。
ところが、iArray[3] は、存在しない要素 です。
ですので、ここで、ArrayIndexOutOfBoundsException が、起きるべきところなのですが、
このコードは、
  try{ このコード } の、中で実行
されています。
ですので、例外を発生させる代わりに、
  aE: ArrayIndexOutOfBoundsException
    (aEインスタンス ということ)
を、投げます。
どこで、キャッチするのかというと、
  catch (aE: ArrayIndexOutOfBoundsException) { 対策コード }
で、
  aE: ArrayIndexOutOfBoundsException
を、キャッチして、対策のコードを、書きます。

ここでは、
  [3] には、要素がありません
と、コンソールに出力して、説明しています。

こんな風に、
  try~catch
を、使って、例外が起こりうるコード を、ラップする/包む わけです。


その他の、実行時例外
あと、ひとつ、
  NumberFormatException
    / この 文字列 は、数値 に変換できません、、例外
を、対策するコードを含めてみてみましょう。

まず、は、対策していないとどうなるか?、を、こんなコードで、確認します。

まず、
  Stringインスタンス.toInt()
とは、
  文字列 を、Int型 に、変換する、String型インスタンス の持つ、関数 ですが、
  何でもかんでも、Int型 に、変更できません。
例えば、
  "Thank you"
などは、絶対できないと思いますが、、今試しにやって見たら、案の定、
  NumberFormatException
が、発生しました。

このコードでは、
  ひょっとして、"twenty" なら、出来るかもしれない
と、思って試したのですが、
残念、出来なくて、NumberFormatException が、発生してしましました。

さて、このコードの 例外 を、発生させなくするためには、
  try~catch
を、使って、どんなコードにすれば、いいと思いますか?
出来れば、自分でも考えてみましょう。
こんなコードにすれば、プログラムは、最悪の、落ちるからは、逃げられます。
このコードの仕組みは、このコード と、同じ仕組みですので、
考えてみてください(try~catch の使い方が)
1行目の、
  import java.lang.NumberFormatException
これは、IntelliJ が、勝手に挿入してくれました。
TryKotlin の場合は、このコードがなくても、普通に動きました。
この違いは、私は分かりませんが、
  AndroidStudio や、IntelliJ、TryKotlin では、
  とりあえず、勝手に、問題を解決してくれる
という事は、言えますね。
この詳細も、分かった方が、いいのは、間違いないですが、
とりあえず、Androidアプリ を、作る際、障害には、ならないでしょう!

最後に、
  例外 が発生しそうな気はするが、
  その例外のクラスの型が分からない場合、どうするか?
という、あまり、褒められた方法ではない、方法ですが、
まあ、「落ちるよりはまし」 という事について考えてみます。
こんな時は、スーパークラス/親クラス という考え方を、使いますが、まだやっていないので、
簡単に説明しますと、
  子供クラス is 親クラス
が、成り立つという事です(その逆は、成り立ちませんが
それが成り立つことを見るためのコードとして、こんなコードを書いてみました。
3行目 を、見てください。
  val letters: Any = "文字列"
普通は、Any などとせずに、String と、
書くところですが、
  Stringインスタンス is Anyクラス型
が、成り立つため、エラー は、発生しません。

という事で、例外クラス の、名前 が分からない時は、
  親クラスである
  Exceptionクラス
  を、使って、とりあえず、例外に対処しましょう
という話です。

具体的には、先ほどのコードを、
  Exceptionクラス
で、対処してみます。
こんなコードで、対処できます。
確かに、対処していますね。
ただ、このやり方は、Kotlinプログラミング が、得意になりたいのであれば、
出来るだけ避けた方が、いいでしょう。
具体的な、例外クラス の名前を使うことを、お勧めします。



まとめ
・例外が、発生した時、スタックトレースを、見れるようにしましょう。
try~catch構文 を、使えるようにしましょう。
・どうしても、例外クラスの名前が分からない場合は、Exceptionクラス が、使えます。
・ここで学習した例外
  ArrayIndexOutOfBoundsException
  NumberFormatException