Beginning Scala programming3

初心者ぺちぱーがGitHubでScalaレッスンを始めたぞ。3回目は例外処理のtrycatchを覚えよう。

Scalaスケーラブルプログラミング第2版
作者
Martin Odersky
Lex Spoon
Bill Venners
出版社
インプレスジャパン
発売日
2011-09-27
メディア
単行本(ソフトカバー)
価格
¥ 4,968

Lessen3: try expressions

引き続き、練習問題はここのページだ。今回の問題は1回目と同じHello Worldを使ってみようと思う。

Hello World![改行]を5回表示させてください。

可能ならコマンドラインから入力を受け取って、n回表示するように改造してください。 何回目のHello World!かも表示してみてください。

Scalaの例外処理はJavaや他の言語と同じく、try-catchを使って発生した例外をキャッチする。ただし、catchの書き方がちょっと違う。

try {
// do hoge
} catch {
case e: HogeException => println("Hogeしますた!")
case e: FugaException => println("Fugaしますた!")
case e: Throwable => println("あーやっちゃった。。")
}

catchの中身は前回やったパターンマッチと同じ書き方になっている。これで例外の型に応じて何かをする、という処理が書けるわけだ。Javaのcatchと比べるとかなりスッキリした書き方よね。

さて、練習問題の、コマンドラインからの引数を受け取るコードを書いてみよう。

def main(args: Array[String]) {
try {
val i = args(0).toInt
if (i > 0) for (n <- 1 to i) println("Hello World! %d".format(n))
else println("arg1: Int > 0")
} catch {
case e: ArrayIndexOutOfBoundsException => println("arg1 is missing")
case e: NumberFormatException => println("arg1: Int")
case e: Throwable => println("failed something")
}
}

case1は引数が指定されていない場合の処理だ。3行目のargs(0)で発生する例外よね。

case2は期待している引数がIntなので、文字列や5.2みたいな値が指定された場合の処理だ。これは5行目のformat(n)のところ。

case3は全部の例外をキャッチしている。このコードでは(多分)発生しないんじゃないかと思う。なんとなく書いてみた。ここはプレースホルダーを使って、こう書いても大丈夫。

case _ => println("failed something")

finally expression

try式が評価された時に、例外が発生する、しないに関わらず、finally式を付けておくと、必ずこの中身が実行される。これはJavaと同じよね。それから、Scalaのtry-catchは値を返すので、こんな書き方が出来る。

println(try {
println("try")
"a".toInt
println("できた?")
} catch {
case e: NumberFormatException => -1
} finally {
println("finally")
})

これを実行すると、先に"try"、"finally"と表示された後で、-1が表示される。"できた?"は、直前の行で例外が発生しているので、表示されない。

実行結果
try
finally
-1

The finally expression e is expected to conform to type Unit

でもfinally自体は値を返さないので、次のコードを実行しても0は表示されず、9だけが表示される。

println(try 9 finally 0)
実行結果
9

値が返ってくるのなら、変数に入れることもできる。

val i = try 9 finally 0

ぬぬぬ!?なんかエラーメッセージが表示されたぞ。

実行結果
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
val i = try 9 finally 0
^
i: Int = 9

finallyは値を返さないので、この場合の0は意味ないよね、というメッセージだ。printlnしたときは出てこなかったのに、なんでだろう。

What if a finally expression throws an exception?

finallyの中で例外が発生すると、この例外をキャッチしていない場合には、発生した例外がスローされる。

val list = List(0, 1)
try {
println(list)
} finally {
println(list(3))
}
実行結果
list: List[Int] = List(0, 1)
List(0, 1)
java.lang.IndexOutOfBoundsException: 3
...

tryでもfinallyでも例外が発生すると、finallyで発生した例外がスローされるようだ。tryではjava.lang.IndexOutOfBoundsException: 3が発生しているはずだが、表示されているのはjava.lang.IndexOutOfBoundsException: 2になった。

val list = List(0, 1)
try {
println(list(3))
} finally {
println(list(2))
}
実行結果
list: List[Int] = List(0, 1)
java.lang.IndexOutOfBoundsException: 2
...

Conclusion

3回目のレッスンもGitHubに上げてある。今回はまとめっぽいまとめだな。色々出てきたけど、Scalaで例外処理を覚えたよ!やったね!

  • try-catchで例外をキャッチする
  • catchはパターンマッチで書く
  • finallyは必ず評価される
  • trycatchは値を返すけど、finallyは値を返さない
  • finallyで例外が発生すると、その例外がスローされるから気をつけてね