例外とは
プログラム実行時に発生したエラー(予想外の動作)のこと
例えば以下のような動作のことをいう
- 存在しないメソッドが呼び出された
- 存在しないカラムにアクセスした
- APIでリクエストを投げたがレスポンスが返ってこない
例外が発生した際は、そのままプログラムを走らせると予期しない結果になる可能性があるため、例外に応じてリカバリ処理を入れたり処理をスキップさせるなどの対応が必要になる(例外処理)。
例外をつかまえる(例外を処理する)
- begin〜rescue〜end
begin
#例外を発生させる可能性のある処理
rescue [捕まえたい例外]
#例外が起こった場合の処理
end
rescueの第1引数には捕まえたい例外クラスを指定
第1引数で指定した例外クラスの下の階層にある例外だけを捕捉する。
デフォルト(引数省略時)は、StandardErrorクラスを指定したとみなす。
例外を発生させる
- raiseメソッド
Kernelモジュールで定義されているメソッド。
# 引数に文字列を指定した場合
# ==> 文字列をメッセージとする`RuntimeError`例外を発生させる
raise "エラー発生"
RuntimeError: エラー発生
#引数に例外クラスを指定した場合
# 指定した例外クラスの例外を発生させる
raise ArgumentError
ArgumentError: ArgumentError
独自の例外クラスをつくる
独自の例外クラスとは?
Rubyで用意されている標準例外クラスとは別で独自の例外クラスを使いたい時に、独自例外クラスを定義して任意の例外に対して独自の例外を発生できるようにする。
独自の例外クラスを作成したいときは、
独自の例外クラスをつくるには、StandardError
を継承する(推奨)。
例
・独自例外クラスを定義
class XXXApiTimeException < StandardError
end
・独自例外を発生させ、捕捉する
begin
xxxxxx #APIがタイムアウトするような例外が発生する処理
raise XXXApiTimeException
rescue XXXApiTimeException
xxxxxx #リカバリ処理
end
継承にStandardErrorクラスが推奨される理由
例外クラスを実装する場合には、標準の例外クラスのどれかを継承すれば良いとあるのになぜStandardError
の継承が推奨されるのか?
そもそも標準例外クラスとは?
階層は以下の通り。
Exception #最上位
|--NoMemoryError
|--ScriptError
| |--LoadError
| |--SyntaxError
|
|--StandardError #独自例外クラスを生成する際に継承を推奨されているクラス
| |--ArgumentError
| |--NameError
| | |--NoMethodError
| |
| |
| |--RuntimeError
| |--TypeError
| |--ZeroDivisionError
|
|--SystemExit
|--SystemStackError
- Exceptionクラスを継承して独自例外クラスをつくるとどうなるのか
上記の例外クラスの階層を踏まえて、例えばStandardError
クラスよりも上位となるException
クラスを独自例外クラスの継承クラスとする場合を考えてみる
class TestError < Exception #独自例外クラス
end
begin
p "hello world"
raise TestError
rescue Exception => error
p "例外をキャッチしたよ"
p error
end
# 結果
"hello world"
"例外をキャッチしたよ"
#<TestError: TestError>
class TestError < Exception #独自例外クラス
end
begin
p "hello world"
test = 1/0 #0で除算するとZeroDivisionErrorが発生
raise TestError
rescue Exception => error
p "例外をキャッチしたよ"
p error
end
#結果
"hello world"
"例外をキャッチしたよ"
#<ZeroDivisionError: divided by 0>
上記の例1では、TestError
クラスの例外を捕捉、例2ではZeroDivisionError
を捕捉している。ここで問題なのは、Exception
クラスをrescue
で捕捉する場合、捕捉する対象が全ての例外クラスとなるのでrescue
でのリカバリ処理に何を書けばいいか特定できない点にある。
以上からException
クラスを継承した独自例外クラスを作成した場合は、例外処理がコントロールしにくいことがわかる。結果として、その下位のStandardError
クラスの継承が現実的。またrescue
の引数を省略した場合も捕捉するクラスはStandardError
クラスとなっていることもStandardError
クラスの継承を推奨する理由の1つとしても良いと思う。
class TestError < StandardError #独自例外クラス
end