NoMethodErrorはRubyでプログラムを書いていると、最もよく目にするエラーの一つだと思う。
Rubyではメソッドの引数となる値の型を要求しない。
Javaなどの言語では、メソッド定義時に引数の型を指定し、実行時にはその型を引数として渡す必要がある。
しかし、Rubyでは
1 2 3 4 5 |
def method(array) array.each do |array| # 適当な処理 end end |
のように型指定なしでメソッドの引数を指定できる。
ここで求められているのはarrayがeachというメソッドを持っている、ということだけである。
仮にArrayオブジェクトを渡す場合問題ないが、Stringオブジェクトを渡してしまった場合、Rubyは例外を発生させる。
1 2 3 4 |
irb(main):006:0> method([1]) => [1] irb(main):007:0> method("a") NoMethodError: undefined method `each' for "a":String |
Rubyは引数の型が何であろうと、インターフェースさえ一致すれば何を与えても良い。
上の例だと、eachメソッドが使えるオブジェクトであればArrayでなくても良いということ。
与えられた値が何であろうと、実際にメソッド呼び出しを行ってみて(今回のケースではeachを実行してみて)、メソッドがない場合は例外を発生させるのがRubyの仕様である。
(これは悪いものではなく、上の例だと、Stringオブジェクトが入ってきたことを教えてくれている。)
しかし、ここで厄介な問題なのが、
仮にarrayが指し示すものがnilだった場合だ。
nilもオブジェクトなので
NoMethodError: undefined method ‘each’ for nil:NilClass
このようにnilクラスにeachメソッドはないと怒られてしまう。
なぜ厄介な問題かというと、プログラムの実行の中で、意図せずnilが入り込むケースは思いの他多いからだ。
Rubyを書いたことがある人なら、一度は上のエラーに遭遇したことがあるはずだ。
○対策
1 nilを前提に考える
とにかく、nilオブジェクトが入っていることを前提にプログラムを書くこと
簡単なのはnil?メソッドを使うこと。
これはレシーバがnilの場合Trueを返してくれるので、条件分岐してnilの場合、メソッドを実行しない等で対策できる。
2 to_s、to_aなどで変換する
Object#to_sやObject#to_aはかなり便利である。
1 2 3 4 5 6 |
irb(main):009:0* nil.to_s => "" irb(main):010:0> nil.to_a => [] irb(main):011:0> nil.to_i => 0 |
例えば、メソッドの戻り値としてStringを期待するメソッドを作る場合、to_sメソッドを使えば、nilかどうかを意識することなく、常にStringを返すメソッドを作ることができる。
もちろん時と場合によるが、nilへの対策として覚えておくといい。
まとめ
・nilオブジェクトに注意する
・nil?メソッドで確認して条件分岐する
・場合によってはto_sメソッドなどが有効となる