Ruby のコードを読んでいると
class Hoge
class << self
def hello
puts 'hello'
end
end
end
って出てきてびびるんだが、これはなんの暗号ですか?
こいつは特異クラスと言いまして、説明をすると大変長いものです。
まず、なにをしているかというと、これはクラスメソッドを定義するイディオムのひとつです。
http://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fdef.html#class_method
def self.class_method
方式では複数のクラスメソッドをまとめて定義したい場合に都度の self. を書くのが面倒なため、以下の書き方でまとめてクラスメソッドを定義します (Rails がこの書き方を多用していたため、After Rails 世代の人はこうするものだと思ってる節がある。またこれを知らないと Rails 読むときの黒魔術ぽさが上がる)。
class Hoge
class << self
def a_class_method
end
def another_class_method
# 二つ定義しても self つけなくていいから便利!
end
end
end
まずはこれだけわかれば Ruby を使ううえで困ることは少ないでしょう。
リファレンスマニュアルでの特異クラスの説明くらいは読んでみてもいいかもしれません。
しかし、実はこれだけではわかったようでなにもわかっていません。特異クラスについて、真剣に学ぶのであれば
あたりを読むのがよさそうです。さらに理解を深めたいなら、「初めての Ruby」、「プログラミング Ruby (ピッケル本)」、「パーフェクト Ruby」あたりの解説を読んでみるといいでしょう。
Ruby では (クラスではなく) オブジェクトに対して直接固有のメソッドを定義することができ、それを特異メソッドと読んでいます。
オブジェクトへ特異メソッドを定義するには下記のようにします。
hello = 'hello'
def hello.repeat(count = 1)
count.times { print self }
end
hello.repeat 2 #=> hellohello
このとき、repeat メソッドは String クラスのオブジェクト 'hello' に対してのみ定義され、String クラスが拡張されたわけではありません。つまり、別の String クラスのオブジェクトへ 'hoge'.repeat(3)
としても NoMethodError となります。
オブジェクトに特異メソッドを定義するにはもう一つ方法があり、オブジェクトの特異クラスをひきだして、直接特異メソッドを定義することができます。
hello = 'hello'
class << hello
def world
puts "#{self}, world"
end
end
hello.world #=> hello, world
一見クラス定義に近い見た目ですが、これも << hello
というところで hello オブジェクトの特異クラスを引き出しており、あくまでオブジェクトに対しての特異メソッドの定義となっています。
さて、ここでクラス定義のときの特異クラスの利用へ戻ると、
class Hoge
class << self
としています。class 定義のコンテキストでの self とは、Class クラスのインスタンス Foo class です。class Foo
とはクラスを定義するときの記法ですが、Ruby の中での理解としては、Class クラスのオブジェクトを生成し Foo というグローバルな定数へ代入しています。
つまり以下の二つはほぼ同義です。
class Foo
def hello
puts 'hello'
end
end
foo = Foo.new
foo.hello #=> hello
Foo = Class.new do
def hello
puts 'hello'
end
end
foo = Foo.new
foo.hello #=> hello
ここまでくるともう少し。クラスメソッド Foo.bye というのは、Foo に入っている Class クラスのオブジェクトへの特異メソッドの定義として読むことができます。
Foo = Class.new do
def hello
puts 'hello'
end
end
def Foo.bye
puts 'good bye'
end
Foo.new.hello #=> hello
Foo.bye #=> good bye
これにオブジェクトの特異クラスの引き出しの記法 << をあわせて考えると、最初のイディオムがやろうとしていることがわかってきます。
# def Foo.bye の代わりに、特異クラスで特異メソッドを定義する
class << Foo
def bye
puts 'good bye'
end
end
# 同じことをどうせならクラス定義の中でやってみる
# クラス定義の中で Foo に入ってるインスタンスを取るには self だよね!
class Foo
class << self
def bye
puts 'good bye'
end
end
end
ということで、最初に読んだ形が出てきました。クラスメソッドのための記法があるわけではなく、特異メソッドという仕組みを使って巧みにクラスメソッドが実現されていることがわかりますね。
その後、るびまに多少丁寧にしたバージョンを公開しました。
http://magazine.rubyist.net/?0046-SingletonClassForBeginners