概要
オブジェクトに対してメソッドが呼び出された際にRubyがどの様にメソッドを呼び出しているのかサンプルコードと共にまとめました。
自身のメソッドを呼び出した時の探索方法
class Class1
def hello
puts 'Hello from Class1'
end
end
obj1 = Class1.new
obj1.hello #=> Hello from Class1
p Class1.ancestors #=> [Class1, Object, Kernel, BasicObject]
Rubyのインタプリタはobj1の直接のクラスであるClass1を参照し、そこで定義されているClass1#helloを実行します。
スーパークラスのメソッドを呼び出す際の探索方法
class Class1
def hello
puts 'Hello from Class1'
end
end
class Class2 < Class1; end
obj2 = Class2.new
obj2.hello #=> Hello from Class1
p Class2.ancestors #=> [Class2, Class1, Object, Kernel, BasicObject]
Rubyインタプリタはまずobj2の直接のクラスであるClass2を参照します。
しかし、Class2にはhelloメソッドが存在しないので親クラスであるClass1を参照し、クラス内に定義されているClass1#helloを実行します。
特異メソッドを呼び出した際の探索方法
class Class1
def hello
puts 'Hello from Class1'
end
end
obj1 = Class1.new
def obj1.hello
puts 'Hello From Singleton'
end
obj1.hello #=> Hello From Singleton
Rubyのインタプリタはオブジェクトのクラスより先に特異クラスを参照し、そのクラス内のメソッドを先に実行します。
クラスにモジュールをミックスインした際のメソッドの探索方法
module Module1
def hello
puts 'Hello from Module1'
end
end
class Class1
def hello
puts 'Hello from Class1'
end
end
class Class2 < Class1
include Module1
end
obj2 = Class2.new
obj2.hello #=> Hello from Module1
p Class2.ancestors #=> [Class2, Module1, Class1, Object, Kernel, BasicObject]
includeされたモジュールはincludeしたクラスの親クラスとの間にあらわれるようになります。
Rubyのインタプリタはまず直接のクラスであるClass2を参照しhelloメソッドを探します。しかし、Class2にhelloメソッドは定義されていないので、次にModule1の機能を取り込んだクラスを参照し、そのクラス内のhelloメソッドを実行します。
複数のモジュールがミックスインされている際のメソッドの探索方法
module Module1
def hello
puts 'Hello from Module1'
end
end
module Module2
def hello
puts 'Hello from Module2'
end
end
class Class1
def hello
puts 'Hello from Class1'
end
end
class Class2 < Class1
include Module1
include Module2
end
obj2 = Class2.new
obj2.hello #=> Hello from Module2
2つ以上のモジュールが1つのクラスにincludeされている場合にはincludeした順序が後のモジュール内のメソッドが優先的に呼ばれるようになります。
モジュールがプリペンドされている際のメソッドの探索方法
Module#prependはModule#includeのように、クラスやモジュールに別のモジュールの機能を取り込むためのメソッドです。
以下がModule#prependの特徴です。
-
Module#includeで取り込んだメソッドと同名のメソッドがModule#prependで入れたモジュールに定義されていた場合Module#prependで入れたメソッドが優先して呼ばれます。 -
Module#prependで組み込んだモジュールは組み込んだクラスの前におかれます。
module Module1
def hello
puts 'Hello from Module1'
end
end
module Module2
def hello
puts 'Hello from Module2'
end
end
module Module3
def hello
puts 'Hello from Module3'
end
end
class Class1
def hello
puts 'Hello from Class1'
end
end
class Class2 < Class1
prepend Module3
include Module1
include Module2
def hello
puts 'Hello from Class2'
end
end
obj2 = Class2.new
obj2.hello #=> Hello from Module3
p Class2.ancestors #=> [Module3, Class2, Module2, Module1, Class1, Object, Kernel, BasicObject]
上記でも記しましたように、Module#prependで取り入れられたモジュールは直接のクラスClass2やincludeされたModule1やModule2よりも優先して参照され、その中で定義されているhelloメソッドが呼び出されます。
オブジェクトに存在しないメソッドを呼び出した場合
module Module1
def hello
puts 'Hello from Module1'
end
end
class Class1
def hello
puts 'Hello from Class1'
end
end
class Class2 < Class1
include Module1
end
obj2 = Class2.new
obj2.goodmorning #=> undefined method `goodmorning'(NoMethodError)
p Class2.ancestors #=> [Class2, Module1, Class1, Object, Kernel, BasicObject]
Rubyのインタプリタはメソッドが見つかるまでクラスの継承ツリーを辿っていきます。
そして継承ツリーを一番上(BasicObject)まで辿ってもメソッドが見つからない場合にはBasicObject#method_missingを呼び出します。
メソッド探索方法まとめ
- (1) Rubyのインタプリンタはレシーバの特異クラスを参照しメソッドを探す。
- (2) 特異クラスが存在しない場合はレシーバの直接のクラスを参照しメソッドを探す。
- (3) モジュールが
includeされている場合その中からメソッドを探す。 - (4) 直接のクラスにメソッドがなくモジュールも
includeされていない場合、継承した親クラスを参照しメソッドを探す。 - (5) 継承ツリーの一番上のクラスまで辿ってもメソッドが見つからない場合、
BasicObject#method_missingを呼び出す。





