Ruby の特異メソッド・特異クラスとはなんなのか?
まずは特異メソッドから。
Rubyでは、クラスではなくオブジェクトに固有のメソッドを定義できる仕組みがある。これを特異メソッドと呼ぶ。
例を見てみよう。
code:rb
#文字列を代入した変数を定義
hoge = 'hoge'
#固有のメソッドを定義できる
def hoge.say
puts 'hogehoge'
end
#定義したメソッドを実行できる
hoge.say #=> hogehoge
こんな感じで、特定のオブジェクトだけにメソッドを定義できる。このとき、Rubyは特定のオブジェクト固有のクラス(特異クラス)を作成して継承チェーンに組み込まれている。
大元となるStringクラスが拡張されたわけではない。他の文字列オブジェクトにsayメソッドを実行しても NoMethodError が出る。
code:rb
'bar'.say #=> undefined method `say' for "bar":String (NoMethodError)
def object.method_nameのように特異メソッドを定義することを「特異メソッド形式」と呼ぶことがある。また、特異メソッドは singleton method とも呼ばれる。
シングルトンとは、オブジェクト指向プログラミングにおけるクラスのデザインパターンの一つで、実行時にそのクラスのインスタンスが必ず単一になるよう設計すること。
https://e-words.jp/w/シングルトン.html
特異メソッド形式以外に、sayメソッドは以下のようにも定義できる。
code:rb
hoge = 'hoge'
#classを使っている
class << hoge
def say
puts 'hogehoge'
end
end
hoge.say #=> hogehoge
class << hoge と宣言することで、クラスのインスタンスメソッドを定義するように、sayというメソッドを定義できているのがわかる。
このような定義方法を、「特異メソッド形式」に対して「特異クラス形式」と呼ぶ。hogeオブジェクト固有のクラス、つまり特異クラスを定義して、そのなかでメソッドを定義している。
ここで「あれ?クラスメソッドの定義方法と似てない?」と気づいた方がいるかもしれない。
そのとおりで、Rubyのクラスメソッドは実質的に特異メソッドである。(より正確には、クラスオブジェクトの特異メソッド)
code:rb
class Hoge
class << self
def say
puts 'saysay'
end
end
end
Hoge.say #=> saysay
クラス内で使われる self は、Class クラスのインスタンスの Hoge class を指す。
code:rb
puts Hoge.new.class #=> Hogeクラス
puts Hoge.new.class.class #=> Classクラス
Rubyでは、クラスはClassクラスのインスタンスという仕組みになっている。Classクラスのオブジェクトが生成されて、Hogeというグローバル定数へ代入されている。
つまり、以下の2つは同義になる。
code:rb
#1 いつもどおりクラスを定義している
class Hoge
class << self
def say
puts 'saysay'
end
end
end
#2 ↑は実際には以下の作業をしている。Hogeというグローバル定数に新たなClassクラスのオブジェクトを代入している
Hoge = Class.new do
class << self
def say
puts 'saysay'
end
end
end
では、#2の形式で作ったHogeクラスに新たなメソッドを特異メソッド形式で追加してみると
code:rb
Hoge = Class.new do
class << self
def say
puts 'saysay'
end
end
end
def Hoge.yay
puts 'yayay'
end
Hoge.yay #=> yayay
Hogeクラスに新たなメソッドが追加され、クラスメソッドとして定義されているのがわかる。HogeもClassクラスのオブジェクトのひとつなので、Hoge.yayといった形で特異メソッドを定義できる。
よって、Rubyではクラスメソッドは実質的に特異メソッドだということがわかる。
特異クラスとは
code:rb
class << インスタンス
end
と特異メソッドを定義する部分。「特異クラス定義式」と呼ばれる。
この部分一体が特異クラスとなる。classクラスのインスタンスに対して特異クラスを定義している
code:rb
class Foo
def hi
puts 'hi'
end
end
Foo.instance_methods false
=> :hi
#fooに特異クラス定義式を使って特異メソッドを定義する
class << foo
def bye
puts 'bye'
end
end
#両者は同一のオブジェクトではないことがわかる
foo = Foo.new => #<Foo:0x000000013b155810>
foo.singleton_class => #<Class:#<Foo:0x000000013b155810>>
#特異メソッドが定義されている
foo.singleton_methods
=> :bye
#fooの特異クラスのインスタンスメソッド
foo.singleton_class.instance_methods false
=> :bye
参考資料
Ruby 初級者のための class << self の話 (または特異クラスとメタクラス)
【Ruby】特異メソッド・特異クラスを理解する - Qiita
コラム - RubyビギナーのためのStep upコラム | 第12回 クラスインスタンス変数と特異クラス・特異メソッド(3)|CTC教育サービス 研修/トレーニング
Ruby のメソッドやオブジェクトまわりの名称について軽くまとめてみた - Secret Garden(Instrumental)