せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】メタクラス。クラスメソッド定義の意味。

 
ここを参考にメタクラスをもう一度ふりかえり。

クラスメソッドの定義方法

この2つの方法はどちらも同じ意味となる。

class MailTruck
  def self.add( truck )
    @@trucks << truck
  end
end

class MailTruck
  class << self  #<= 特異クラスをオープン
    def add( truck )
      @@trucks << truck
    end
  end
end

 
この例では、addはClassクラスオブジェクト(MailTruck)の特異メソッド。

Rubyでは必ず、メソッドはオブジェクトではなくクラスによって保持される。
→これはあくまで実装上そうだというだけで本質的な理解ではない。
メタクラスは「特異メソッドのロード(スーパークラスのメソッドをなぜ名前解決できるのか)」
の文脈だけでしか語らないようにする。
(10月15日追記)


つまり、クラスメソッドはそのクラスの特異クラス(メタクラス)によって保持される。
さっきの下の例が分かり易い。
特異クラスをオープンして、そこにメソッドを追加している。

 
メタクラスは継承チェインに割り込んで、
クラスのメソッドより先に呼び出されるようになる。
また、メタクラスも(クラスと同じように)スーパークラスをもつ。


 

子クラスにメソッドを追加する

Objectにメソッドを追加して、

class Object
  # 特異クラスはどこにでも隠れてる。
  def metaclass; class << self; self; end; end
  def meta_eval &blk; metaclass.instance_eval &blk; end
  # メタクラスにメソッドを追加
  def meta_def name, &blk
    meta_eval { define_method name, &blk }
  end
  # クラスの中でインスタンスメソッドを定義
  def class_def name, &blk
    class_eval { define_method name, &blk }
  end
end

 
こうやって書くと、子クラスにメソッドを定義できる。
親クラスのcompメソッド内のコンテキストが
HappyTruckクラスオブジェクトになるから、まあそうだよね。

class MailTruck
  def self.comp( name )
    meta_def :company do
      name
    end
  end
end
class HappyTruck < MailTruck
  comp "Happy's -- We Bring the Mail, and That's It!"
end
p HappyTruck.company  #<= "Happy's -- We Bring the Mail, and That's It!"

 

上のメソッドはきわめてシンプルだが、無限の可能性を秘めている。

そうなんだー。