一から勉強させてください

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです

Rubyのメソッド探索についてまとめてみた

今回はパーフェクト Ruby で「Ruby のメソッド探索」について学んだのでそのメモです。

オブジェクトに対してメソッド呼び出しが行われる際、どのようにメソッドが呼び出されているのか、書いてみたいと思います。

自身のメソッドを呼び出した時

class BaseClass
  def hello
    :hello
  end
end

base_object = BaseClass.new
base_object.hello #=> :hello

f:id:d_animal141:20141223140446p:plain

これは最も単純なケース。 自身のクラスのhelloメソッドを探しにいって、あれば実行します。

親クラスのメソッドを呼び出した時

class InheritClass < BaseClass
end

inherit_object = InheritClass.new
inherit_object.hello #=> :hello

f:id:d_animal141:20141223141122p:plain

この場合、まず Rubyインタプリタinherit_objectのクラスInheritClassを参照しますが、ここにhelloメソッドはありません。なので、次にその親クラスであるBaseClassを探しにいってそこで見つかったhelloメソッドを呼び出します。

特異メソッドを呼び出した時

def base_object.hello
  :singleton_method_hello
end

base_object.hello #=> :singleton_method_hello

f:id:d_animal141:20141223142442p:plain

base_objectの特異メソッドにhelloを定義した場合。これはbase_objectの特異クラスのメソッドなので上図のような関係になります。 特異クラスはオブジェクトのクラスよりも先にメソッド探索されるようになっています。よって今回の場合、インタプリタはまずbase_objectの特異クラスを参照し、そこにあるhelloメソッドを呼び出します。

ちなみにbase_objectの特異クラスにはbase_object.singleton_classでアクセス可能(特異クラスはそれを定義したり、確認しようとしたタイミングで作成される)です。

クラスに include したモジュールのメソッドを呼び出した時

module HelloModule
  def hello_from_module
    :hello_from_module
  end
end

class InheritClass
  include HelloModule
end

inherit_object = InheritClass.new
inherit_object.hello_from_module #=> :hello_from_module

f:id:d_animal141:20141223144035p:plain

モジュールをクラスにincludeした場合、そのモジュールの機能を取り込んだクラスが自身と親クラスの間に挿入されます。そして自分、モジュールの機能を取り込んだクラス、親クラスの順に探索します。

今回の場合はモジュールの機能を取り込んだクラス内でhello_from_moduleを発見し、それを呼び出しています。

ちなみに複数のモジュールをincludeした場合は後からincludeしたモジュールが前に挿し込まれる感じになります。

クラスに extend したモジュールのメソッドを呼び出した時

module HelloModule
  def hello_from_module
    :hello_from_module
  end
end

class InheritClass
  extend HelloModule
end

inherit_object = InheritClass.new
inherit_object.hello_from_module #=> NoMethodError: undefined method ...
InheritClass.hello_from_module #=> :hello_from_module

f:id:d_animal141:20141223151354p:plain

モジュールをクラスにextendした場合、レシーバ(今回はInheritClass)の特異クラスに対してextendしたモジュールがincludeされます。 よって上図のような関係になり、モジュールで定義しているメソッドがInheritClassのクラスメソッドとして使えるようになります。inherit_objectのメソッド探索は直線部なので、破線部にあるhello_from_moduleは見つかりません。

存在しないメソッドを呼び出した時

method_missingメソッドが定義されているクラスを探し続けます。 それが見つからない場合は最終的にBaseObject#method_missingを呼び出し、NoMethodErrorが発生します。

まとめ

特異メソッド、特異クラス、mixin など何気なく使っているものの動作を図で整理できてよかったです。 なにか間違えていたらご指摘いただけると幸いです。

今回も参考にしたのはこちら。