Help us understand the problem. What is going on with this article?

特異クラス・特異メソッド・メソッドの種類 Ruby

More than 5 years have passed since last update.

オブジェクトに処理を行わせる、何らかの命令を定義する時に メソッドを使う。
メソッドには種類があって、インスタンスメソッド、クラスメソッド、関数的メソッド、特異メソッド、モジュールメソッドがある。

インスタンスメソッド はレシーバをつけて呼び出すメソッドである。レシーバはどこかのクラスのインスタンスである

class Cat
  def siro
    "siro"
  end
end

a = Cat.new
a.siro
# => "siro"

"aa".length
# => 2

クラスメソッド はレシーバがクラス名つまり、定数で指定して呼び出せるメソッドである

class Zoo

  def self.gorira
    "gorira"
  end

end

Zoo.gorira
# => "gorira"

関数的メソッドはレシーバ省略形式で呼ばれるメソッドのことである
irbを起動してみると

> puts "a"
> p "a"

aというメッセージが返ってくる、

また、クラス定義の中でprivateで指定したメソッドのこと。クラス定義の中でprivateでメソッドを指定すればそのメソッドはレシーバを省略した形でしか使えないメソッドになる

class Cat

  def tunakan
    secret_tunakan
  end

  private
  def secret_tunakan
    "delicious"
  end
end

a = Cat.new
a.tunakan
# => "delicious"
Cat.ancestors #クラスの継承関係を表示するメソッド
=> [Cat, Object, Kernel, BasicObject]

ちなみにp, print, putsはObjectクラスのPrivateなメソッドでクラスを定義するとObject、Kernel、BasicObjectが継承関係に取り込まれるからレシーバを省略できる。

> Object.private_methods.grep(/p/)
=> [:prepended, :initialize_copy, :public, :protected, :private, :initialize_dup, :sprintf, :loop, :respond_to_missing?, :open, :printf, :print, :putc, :puts, :p, :trap, :spawn, :sleep, :proc, :Complex]

モジュールメソッドは特殊なクラスメソッドだと思えばいいかもしれない、

module Panda
  def self.panda
    "panda"
  end
end

class Zoo
  include Panda

  def self.gorira
    "gorira"
  end

end


Panda.panda
# => "panda"

最後に 特異メソッドであるがこれはメソッドを特定のオブジェクトに追加する方法である

a = [1,2,3,4,5]
b = [1,2,3,4,5]

def a.jijyou
  self.map { |b| b * b  }
end

a.jijyou
=> [1, 4, 9, 16, 25]

b.jijyou
=> NoMethodError: undefined method `jijyou' for [1, 2, 3, 4, 5]:Array

上記の例ではオブジェクトが違うため変数bはエラーがでた、このように特定のオブジェクトに紐づくのが特異メソッドである、
オブジェクトにはクラス名の定数も含まれるから クラスメソッドとはクラスの特異メソッドであるという事実に気づく、非常にややこしいけど、慣れるしかない。

特異メソッドは下記の例からobjectの部分はオブジェクトの参照、クラス名の定数、リテラル表記、selfが入る。

def object.method # オブジェクトの参照 or クラス名の定数 or リテラル表記 or self
  # 処理の中身
end

オブジェクトの概念 Rubyの章でオブジェクトはクラスへの参照とインスタンス変数をもっていて、メソッドは持たないと書いたが、特異メソッドは特定のオブジェクトに紐づいていてそれってオブジェクト自身がメソッドをもっていることにはならないか?疑問に思わないだろうか、、特異メソッドはインスタンメソッドでもない、 コマンドを打ってみてクラスに問い合わせても特異メソッドは見つからない

a = "nyaaa"
def a.dog
  "wanwan"
end

a.dog
# => "wanwan"

a.class.instance_methods(false).grep(/dog/)
# => [] #空っぽ
a.class.superclass.instance_methods(false).grep(/dog/)
# => []

Stringクラスには定義されていないという解答が返ってきた、
実はクラスメソッドの場合も同様であるがRubyは裏で特別なクラスを持っていて、 特異クラス と呼ばれているクラスを持っている

特異クラスとは特異メソッドのためにあるクラスである

obj = "singleton_class"

singleton_class = class << obj
  self
end

singleton_class.class
=> Class

変った構文で特異クラスの正体を見つけることができる、上記の例から特異クラスもクラスの一員であることが分かる

def obj.won_won
  "gau!!!"
end

singleton_class.instance_methods.grep(/won/)

# => [:won_won]

特異メソッドは特異クラスに住んでいていることが分かる、特異メソッドはインスタンスを1つしか持てなくて、シングルトンクラスとも呼ばれている

class Object
  def eigenclass
    class << self
      self
    end
  end
end

obj.eigenclass
=> #<Class:#<String:0x007ff8416d03f8>>

obj.eigenclass.superclass
=> String

Objectクラスに特異クラスを返すメソッドを定義する、
上記の例からobj.eigenclassを呼び出すことによってobjの特異クラスはStringクラスであり、その親もStringクラスであることが分かる。
前者を特異クラスのStringクラスとして、後者を通常のメソッドの探索に含まれるStringクラスと思ってもらって良い
また、Rubyのプログラムは最初に特異クラスにメソッドがあるか問い合わせてその後に通常のクラスに問い合わせる

さらに下記の例を使ってクラスと特異クラスの関係を探ってみる

  class Animal
    class << self
      def mammal
        "cat"
      end
    end
  end

  class Cat < Animal
  end


  Animal.eigenclass
=> #<Class:Animal>

  Cat.eigenclass
=> #<Class:Cat>

  Animal.eigenclass.superclass
=> #<Class:Object>

  Cat.eigenclass.superclass
=> #<Class:Animal>


Animalクラスの特異クラスは特異クラスAnimalであり、特異クラスAnimalの親は特異クラスObjectである、またCatクラスの特異クラスは特異クラスCatであり、その親は特異クラスAnimalである
複雑で分かりにくいがこの仕組みのおかげでAnimalクラスで定義したクラスメソッドがつまり特異メソッドが子のCatクラスでも使えるのだ

  Cat.mammal
=> "cat"

まとめ

メソッド探索:メソッドを探す時Rubyはまずそのオブジェクトの所属するクラスに問い合わせる、見つからなければ上へ上へ向かってメソッドを探していく
インスタンスメソッド はレシーバをつけて呼び出すメソッドである
クラスメソッド はレシーバがクラス名つまり、定数で指定して呼び出せるメソッドである
関数的メソッドはレシーバ省略形式で呼ばれるメソッドのことである
モジュールメソッドは特殊なクラスメソッド
特異メソッドはメソッドを特定のオブジェクトに追加する方法である、クラスメソッドも特異メソッドの一種である
特異クラスは特異メソッドやクラスメソッドをもっていて、インスタンスは1つしか持てない、特異メソッドは特異クラスのインスタンスメソッドである

続く、、、、

fukumone
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away