ゲゲ、エラーが発生した。プログラムが異常終了する!
エラーが発生したら異常終了してしまう言語が多いのですが、Smalltalkの場合むしろエラーが発生してからが勝負です。何ができるでしょうか?
オブジェクト指向3ナイ運動の番外編として、例外オブジェクトが受け付ける代表的なメッセージを紹介します。
pass, resignalAs:
さらに上位(外側)の例外ハンドラに処理を委ねます。resignalAs:の場合、引数として渡された例外として上位の例外ハンドラに処理を委ねます。普通はそのハンドラでの責任範囲の後始末をつけたりした上で、passメッセージを投げます。
resume, resume:
例外を投げたメッセージ式(つまり、anException signal等)の返り値を指定して、実行を継続します。例えばベクトルの単位ベクトルを計算しようとしてゼロ割が発生したらそのままデフォルトの単位ベクトルを返り値として計算を継続したり。これを条件分岐ではなく例外処理で行うことができます。
([ 1 / 0 + 2 ] on: ZeroDivide do: [ :ex | ex resume: 3 ]) "==> 5"
return, return:
ハンドラを設定した対象のクロージャの返り値を指定して、実行を継続します。resume:と違って、クロージャ全体の返り値になるところが要点です。
([ 1 / 0 + 2 ] on: ZeroDivide do: [ :ex | ex return: 3 ]) "==> 3"
retry, retry:
ハンドラを設定した対象のクロージャの実行を放棄して、その代わりに指定したクロージャ(もしくは元のクロージャ)を再実行します。ネットワークが切れていたら繋ぎ直してみるとか、そんな感じで使ったり。
([ 1 / 0 + 2 ] on: ZeroDivide do: [ :ex | ex retryUsing: [ 1 + 2 ] ]) "==> 3"
freeze
例外オブジェクトをハンドラ外にエスケープさせるために実行コンテキストを凍結します。
例えば下のコードでは、ハンドラ外で例外オブジェクトに"receiver"というメッセージを投げて、どのオブジェクトが例外を発生させたのかを得ます。
([ 1 / 0 ] on: ZeroDivide do: [ :ex | ex freeze ]) receiver "==> 1"
debug
Exceptionクラスにdebugというメソッドが定義されています。つまり、例外が発生したらその例外をcatchしてその例外にdebug"というメッセージを投げれば対話的デバッガが開きます。Smalltalkでのデフォルト動作。
[ 1 / 0 ] on: ZeroDivide do: [ :ex | ex debug ]