15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Ruby】メソッドの探索方法を理解する

15
Last updated at Posted at 2018-07-26

概要

オブジェクトに対してメソッドが呼び出された際に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を実行します。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 11.24.59.png

スーパークラスのメソッドを呼び出す際の探索方法

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を実行します。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 11.22.49.png

特異メソッドを呼び出した際の探索方法

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のインタプリタはオブジェクトのクラスより先に特異クラスを参照し、そのクラス内のメソッドを先に実行します。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 11.34.57.png

クラスにモジュールをミックスインした際のメソッドの探索方法

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メソッドを探します。しかし、Class2helloメソッドは定義されていないので、次にModule1の機能を取り込んだクラスを参照し、そのクラス内のhelloメソッドを実行します。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 13.13.26.png

複数のモジュールがミックスインされている際のメソッドの探索方法

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した順序が後のモジュール内のメソッドが優先的に呼ばれるようになります。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 13.39.42.png

モジュールがプリペンドされている際のメソッドの探索方法

Module#prependModule#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で取り入れられたモジュールは直接のクラスClass2includeされたModule1Module2よりも優先して参照され、その中で定義されているhelloメソッドが呼び出されます。

参照順序は以下の図の通りです。
Screen Shot 2018-07-26 at 13.41.44.png

オブジェクトに存在しないメソッドを呼び出した場合

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を呼び出す。
15
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?