ここまで、例外の仕組みの概要と、例外を発生させる方法について紹介しました。ここでは、例外を捕捉して処理する方法について解説します。
例外を捕捉するための基本形を、exception02.rbに示します。
begin raise "something wrong" rescue => e puts "An exception is occurd!" p e p $! end
$ ruby exception02.rb An exception is occurd! #<RuntimeError: something wrong> #<RuntimeError: something wrong>
例外を捕捉したいコードを「begin〜rescue」の間に記述し、例外が起きた場合の処理を「rescue〜end」の間に記述します。また、「rescue => e」のように記述することで、rescue〜end間で変数eに例外オブジェクトが格納されます。また、グローバル変数「$!」を参照することで例外オブジェクトを扱うこともできます。
ここでは、2行目でraiseメソッドを使ってRuntimeErrorを発生させているので、実行結果の3行目でRuntimeErrorと、raiseメソッドに設定したメッセージ「something wrong」が出力されていることが分かります。
基本形で紹介した方法では、例外クラスの種類に応じて処理を振り分けたい場合、rescue〜end間でif文などを使って、例外オブジェクトのクラスを参照して処理を分岐させる必要があります。実は、そのようなことをしなくても、exception03.rbのように記述すれば、例外オブジェクトのクラスに応じて処理を振り分けられます。
require_relative "exception01" begin teapot = Teapot.new("coffee") puts teapot.pour_out rescue UnacceptableRequidError => e puts "Rescued in 'UnacceptableRequiedError => e'" p e p e.obj rescue NoMethodError, ZeroDivisionError => e puts "Rescued in 'rescue NoMethodError, ZeroDivisionError => e'" p e end
$ ruby exception03.rb Rescued in 'UnacceptableRequiedError => e' #<UnacceptableRequidError: unacceptable> "coffee"
require_relativeでexception01.rbのコードを利用するので、exception03.rbはexception01.rbと同じディレクトリに配置してください。
例外クラスに応じて処理を振り分ける場合は、6行目や9行目のように、rescueに続けて例外クラスを指定します。また、10行目のように複数の例外クラスを指定することもできます。
ここでは、コードの4行目で、Teapotが許容しない「coffee」でTeapotオブジェクトを生成しようとしています。UnacceptableRequidError自体はTeapotの基底クラス、Vesselのpour_inメソッドの中で発生します。しかし、pour_inメソッドの中では例外を捕捉しないため、コールスタック(呼ばれたメソッドを順々に積んだスタック)をさかのぼっていき、最終的にexception03.rbに記述されているbegin〜end節で捕捉されます。
この例では「Teapot.new("coffee")」で例外が発生するため、5行目は実行されず7行目に処理が移ります。Rubyのインタプリタは複数記述されたrescueを上から順番にチェックし、初めて合致した例外クラスの例外処理に分岐させるため、11〜12行目の処理は実行されません。
また、exception01.rbではUnacceptableRequidErrorで@objというインスタンス変数を設定したことを思い出してください。そのため、コードの9行目と実行結果の4行目のように、どのようなオブジェクトを設定しようとして例外が発生したのかを知ることができます。
もし、rescueに設定されたどの例外クラスにも合致しない場合、exception04.rbのように、例外の捕捉が行われないのでプログラムはその場で停止し、エラーメッセージが出力されます。
require_relative "exception01" begin bottle = Bottle.new("water") bottle.pour_out rescue UnacceptableRequidError => e puts "Rescued in 'UnacceptableRequiedError => e'" p e rescue NoMethodError, ZeroDivisionError => e puts "Rescued in 'rescue NoMethodError, ZeroDivisionError => e'" p e end
$ ruby exception04.rb exception04.rb:4:in `<main>': uninitialized constant Bottle (NameError)
ここでは4行目で、定義されていないBottleクラスを使おうとしています。Bottleクラスは定義されていないので、NameErrorという例外が発生して処理が止まってしまいます。シチュエーションによっては、これでは困ることもあるでしょう。そのような場合、exception05.rbのように、StandardErrorを指定して捕捉するとよいでしょう。
require_relative "exception01" begin bottle = Bottle.new("water") bottle.pour_out rescue UnacceptableRequidError => e puts "Rescued in 'UnacceptableRequiedError => e'" p e rescue NoMethodError, ZeroDivisionError => e puts "Rescued in 'rescue NoMethodError, ZeroDivisionError => e'" p e rescue StandardError => e # rescue => e でもOK puts "Rescued in 'rescue StandardError => e'" p e end
$ ruby exception05.rb Rescued in 'rescue StandardError => e' #<NameError: uninitialized constant Bottle>
exception04.rbの記述に加え、12〜14行目にStandardErrorを捕捉するrescueを記述しています。例外クラスの合致判定を行うとき、発生した例外クラスの基底クラスも対象とします。NameErrorはStandardErrorから派生しているため、無事12行目で捕捉できます。
また、コード中のコメントに書いている通り、StandardErrorを捕捉する場合は、「rescue => e」というように省略して書くことができます。
Copyright © ITmedia, Inc. All Rights Reserved.