オーバーライドできるtry-catch-finally
注意
タイトルは嘘です。
正確にはtry-catch-finallyらしきもの。
概要
try-catch-finallyをメソッドでラップし、拡張可能にする。
メソッドチェインで本物に似せる。
動機
printStackTraceなど、お決まりのtry-catch-finallyを書くのが面倒。
基礎知識
必要な知識はこのへん。
- メソッド呼び出しの引数には遅延評価できるブロックが渡せる
- 関数の中身は呼び出されるまで評価されない
- メソッドチェインがドットなしで書ける
方針
Try.apply, Catch, Finally呼び出しで渡されるブロックを関数にしてTryインスタンスに貯めておく。
Finallyの最後にexecuteでまとめて実行する。
拡張できるように、各関数呼び出しの部分はexecTry, execCatch, execFinallyとメソッドにしておく。
Scalaのtryは式で値を返すが、面倒なのでunitに限定する。
package mytry; object TryMain { def Try = new Try() def main(args : Array[String]) : Unit = { Try { println("try") (null : String).toInt } Catch { case e => println("catch: " + e) } Finally { println("finally") } println("normally finished!") } } class Try { //variables to hold blocks var tryPart : Unit => Unit = { _ => () } var catchPart : PartialFunction[Throwable, Unit] = { case e => throw e } var finallyPart : Unit => Unit = { _ => () } //set tryPart def apply(tryPart : => Unit) : Try = { this.tryPart = _ => tryPart this } //set catchPart def Catch(catchPart : PartialFunction[Throwable, Unit]) : Try = { this.catchPart = catchPart this } //set finallyPart and execute def Finally(finallyPart : => Unit) : Unit = { this.finallyPart = _ => finallyPart execute() } //execute real try-catch-finally def execute() = try { execTry() } catch { case e : Throwable => execCatch(e) } finally { execFinally() } def execTry() = tryPart.apply() //execute catchPart if the exception can be caught def execCatch(e : Throwable) = if(catchPart.isDefinedAt(e)){ catchPart.apply(e) } else { throw e } def execFinally() = finallyPart.apply() }
実行結果
try catch: java.lang.NumberFormatException: null finally normally finished!
try -> catch -> finallyと、期待通りの挙動をして正常終了していることがわかる。
ちなみに、(null : String).toIntはNullPointerExceptionではなくNumberFormatExceptionが投げられる。
拡張
execTry, execCatch, execFinallyという拡張できるメソッドを用意したので
オーバーライドしてみる。ブロック呼び出しの前後にprintを挟む。
package mytry; object TraceTryMain { def TraceTry = new TraceTry() def main(args : Array[String]) : Unit = { TraceTry { println("try") (null : String).toInt } Catch { case e => println("catch: " + e) } Finally { println("finally") } println("normally finished!") } } class TraceTry extends Try { //override execs override def execTry() { println("tryPart start") super.execTry() println("tryPart end") } override def execCatch(e : Throwable) { println("catchPart start") super.execCatch(e) println("catchPart end") } override def execFinally() { println("finallyPart start") super.execFinally() println("finallyPart end") } }
実行結果
tryPart start try catchPart start catch: java.lang.NumberFormatException: null catchPart end finallyPart start finally finallyPart end normally finished!
"tryPart end"のみ、例外発生により実行されていないことがわかる。