1から始めるRubyの続きです。

クラス再び

「Rubyの1はFixnumというクラスに属している」。前回私はそう言った。疑い深いあなたはうさぎ本1やキリン本2を紐解いて、その事実を確かめたかもしれない。

でもそれは回り道だ。それも1に聞くのが確実で早い。

>> 1.class
=> Fixnum

しかもこの方法は確実で早いだけではない。実は1の返答には、Rubyにおける別の重大な真実が隠されているのだ。

あなたが1にclassと送ったら、彼はFixnumと返してきた。Rubyでは入口も出口もオブジェクトであった。賢明なあなたはもう気が付いたかもしれない。

そう、RubyではFixnumというクラスもまたオブジェクトなのである!

あなたは誰かに、クラスというのはオブジェクトのひな形、つまり設計図であると教えられただろう。Rubyにおいてもそれは真実だ。Fixnumクラスは1オブジェクトのひな形になっている。でもFixnumはそれと同時にRubyの世界に実体として存在し、1と同じようにオブジェクトとして振る舞うのだ。

試しに前回1に覚えさせた挨拶にFixnumを投げてみよう。

>> class Fixnum
>>   def hello(name)
>>    "Yo! #{name}"
>>   end
>> end
=> nil
>> 1.hello(Fixnum)
=> "Yo! Fixnum"

helloメソッドはFixnumをオブジェクトとして受け入れた。

では1のときと同様に、Fixnumがどんなメッセージに反応するか見てみよう。

>> Fixnum.methods
=> [:allocate, :superclass, :freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, :autoload?, :instance_method, :public_instance_method, :nil?, :=~, :!~, :eql?, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

Fixnumはこれら95個のメッセージに反応する。

ちなみに1のためにFixnumが持っているメソッドを見るには、次のようにすればいい。

>> Fixnum.instance_methods
=> [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo, :divmod, :fdiv, :**, :abs, :magnitude, :==, :===, :<=>, :>, :>=, :<, :<=, :~, :&, :|, :^, :[], :<<, :>>, :to_f, :size, :zero?, :odd?, :even?, :succ, :integer?, :upto, :downto, :times, :next, :pred, :chr, :ord, :to_i, :to_int, :floor, :ceil, :truncate, :round, :gcd, :lcm, :gcdlcm, :numerator, :denominator, :to_r, :rationalize, :singleton_method_added, :coerce, :i, :+@, :eql?, :quo, :remainder, :real?, :nonzero?, :step, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase, :rectangular, :rect, :polar, :conjugate, :conj, :between?, :nil?, :=~, :!~, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
>> Fixnum.instance_methods == 1.methods
=> true

メタクラス(クラスのクラス)

さてFixnumがオブジェクトだとすると、また一つの疑問が湧いてくる。Fixnumにメッセージを送ったとき、その答えを用意しているのは一体誰なのかと。

もうあなたも答えの探し方が分かっただろう。 irbを開いて..

>> Fixnum.class
=> Class

FixnumはClassという名のクラスを返してきた。つまりFixnumにメッセージを送ったとき、それに答える実体はClassクラスであったのだ。

そしてあなたの疑問は尽きることはない。先の答えからClassもまたオブジェクトであり、したがってClassもメッセージに反応する。だとしたらその答えを用意しているのは一体誰なのかと。

さあもう一度やってみて!

>> Class.class
=> Class

自分の目を疑っているなら、これで目が覚めるだろう。

>> Class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class
=> Class

Rubyの世界の家族構成が見えてきた。1オブジェクトにはFixnumクラスという母がいて、1に代わって答えを用意し、Fixnumクラス(オブジェクト)にはClassクラスという母がいて、Fixnumに代わって答えを用意する。そして、Classクラス(オブジェクト)は自分自身が母でもあって、自分で答えを用意する。

image

そうつまりClassクラスはRubyの世界におけるアダムとイヴだったのだ!

世界(オブジェクト)はここからはじまった

前回私は「FixnumこそがGoogle同様のスゲー存在なのである」と言ったが今ここでそれを撤回する。そして言う。

「ClassクラスこそがGoogle同様のスンゲー存在なのである」

クラスメソッド定義

さて、ここまでの仕組みが理解できたならFixnumにも挨拶をさせてみよう。やり方はもうわかるよね?

>> class Class
>>   def bonjour
>>     "Bonjour from Fixnum with love!"
>>   end
>> end
=> nil
>> Fixnum.bonjour
=> "Bonjour from Fixnum with love!"

一見うまくいっているこのコードを見て何も感じないのであれば、あなたはRubyのことをまだよく知らないのかもしれない。このまま先を読み進めてほしい。違和感を感じたのなら「1から始めるRuby(その326)」に飛んで!ここはあなたのためのページじゃない。

問題を明らかにしよう。下のコードを見てほしい。

>> String.bonjour
=> "Bonjour from Fixnum with love!"
>> Array.bonjour
=> "Bonjour from Fixnum with love!"
>> Hash.bonjour
=> "Bonjour from Fixnum with love!"
>> Symbol.bonjour
=> "Bonjour from Fixnum with love!"
>> Regexp.bonjour
=> "Bonjour from Fixnum with love!"
>> Range.bonjour
=> "Bonjour from Fixnum with love!"

RubyにはFixnum以外にも多数のクラスが用意されている。上に並べたものはそれらの代表選手だけれども、これらのクラスもbonjourメソッドに反応してしまった。しかもfrom Fixnumなんて!

実は、ClassクラスはFixnumの母というだけではなく、すべてのクラスの母なのだ。

>> String.class
=> Class
>> Array.class
=> Class
>> Hash.class
=> Class
>> Symbol.class
=> Class
>> Regexp.class
=> Class
>> Range.class
=> Class

仮にあなたが独自のメソッドに飽きたらなくなって後から独自のクラスを作ったときにも、この問題のあるbonjourメソッドに反応してしまう。

>> class Loner
>>
>> end
=> nil
>> Loner.bonjour
=> "Bonjour from Fixnum with love!"

image

解決策は2つある。1つ目はFixnumという文字列の代わりにselfを渡すのだ。

>> class Class
>>   def bonjour
>>     "Bonjour from #{self} with love!"
>>   end
>> end

selfというのはそれを包むオブジェクトを返すRubyのキーワードだ。bonjourメソッドがFixnumクラスオブジェクトに送られたなら、Fixnumがselfになる。Arrayクラスオブジェクトに送られたなら、Arrayがselfになる。あなたのLonerクラスオブジェクトに送られたなら、Lonerがselfになる。

>> Fixnum.bonjour
=> "Bonjour from Fixnum with love!"
>> String.bonjour
=> "Bonjour from String with love!"
>> Array.bonjour
=> "Bonjour from Array with love!"
>> Hash.bonjour
=> "Bonjour from Hash with love!"
>> Symbol.bonjour
=> "Bonjour from Symbol with love!"
>> Regexp.bonjour
=> "Bonjour from Regexp with love!"
>> Range.bonjour
=> "Bonjour from Range with love!"
>> Class.bonjour
=> "Bonjour from Class with love!"
>> Loner.bonjour
=> "Bonjour from Loner with love!"

続き

  1. http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0Ruby-%E2%88%92%E8%A8%80%E8%AA%9E%E7%B7%A8%E2%88%92-Dave-Thomas-Fowler/dp/4274068099?SubscriptionId=06WK2XPKDH9TJJ979P02&tag=keyesblog05-22&linkCode=xm2&camp=2025&creative=165953&creativeASIN=4274068099
  2. http://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AERuby-Yugui/dp/4873113679?SubscriptionId=06WK2XPKDH9TJJ979P02&tag=keyesblog05-22&linkCode=xm2&camp=2025&creative=165953&creativeASIN=4873113679


blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS