學習ruby入門的時候,很容易被其類和模組的小trick給迷惑住了,這裡為了整理自己的理解,就寫出來看看吧。
1.ruby一切皆對象
ruby是徹底地物件導向,你見到的一切構件都是對象。數字是對象,字串是對象,類也是對象,模組也是對象,甚至類的類(Class)也是對象......
irb(main):001:0> 1.is_a? Object=> trueirb(main):002:0> Object.is_a? Object=> trueirb(main):003:0> Class.is_a? Object=> trueirb(main):007:0> class Testirb(main):008:1> end=> nilirb(main):009:0> Test.is_a? Object=> true
其實在這裡,像我這樣從傳統物件導向語言過來的初學者往往會提出疑問:在Java中,我們總可以明顯地看出是那個對象在調用方法,但當我進入irb時,並沒有建立任何對象,怎麼可以調用puts等等方法?這裡先不討論puts方法來源於哪兒,但是當開始執行第一行ruby代碼之前,實際上就存在一個對象了。這個對象叫main,是Object的對象。
irb(main):015:0> self=> mainirb(main):016:0> self.class=> Object
此時所處的環境被稱為Top Level Context(頂級上下文)。是ruby調用棧的頂端,通常(不在TLC時並非main在充噹噹前對象)你調用puts等方法時就是這個main對象在充當調用者(接收者)。
2.ruby的類和模組
ruby的類和模組可以統一看成是對象的分類,ruby中每個對象都有一個分類:
irb(main):017:0> Object.class=> Classirb(main):018:0> t=Test.new=> #<Test:0x1a5aa48>irb(main):019:0> t.class=> Testirb(main):020:0> Kernel.class=> Moduleirb(main):023:0> Module.class=> Classirb(main):024:0> Class.class=> Class
可以看到,對象的分類是其普通類,類的分類是Class,Kernel模組的分類是Module,Class的分類也是Class,Module的分類也是類。
甚至於,在ruby中,我們可以認為只有一種分類,那就是模組Module,因為Class是從Module繼承而來。
irb(main):011:0> Class.ancestors=> [Class, Module, Object, Kernel, BasicObject]
除了很少的幾個區別,幾乎可以將類和模組一視同仁,類只不過是增強後的模組。那麼,ruby中其他普通類和Class/Module有什麼關係呢?答案是,其他類(模組)不過是Class(Module)的執行個體而已。
irb(main):028:0> Test.instance_of? Class=> trueirb(main):013:0> Kernel.instance_of? Module=> true
根據這個原理,就可以像下面這樣定義新類:
irb(main):029:0> MyClass=Class.new doirb(main):030:1* def sayirb(main):031:2> puts "I am MyClass"irb(main):032:2> endirb(main):033:1> end=> MyClassirb(main):034:0> m=MyClass.new=> #<MyClass:0x1a4f798>irb(main):035:0> m.sayI am MyClass=> nil
使用Class.new操作就建立了一個類,把這個類賦值給MyClass,這個新類就定義出來了,可以像使用其他類一樣使用。這段代碼除了證明普通類不過是Class的執行個體外,還說明了另一個問題:類名不過是一個常量而已。我們可以查看當前常量列表來驗證:
irb(main):038:0> Object.constants.grep /MyClass/=> [:MyClass]
3.ruby的方法尋找
當在ruby程式中調用一個方法時,ruby解譯器以方法的接收者或者self為起點,沿著該對象的祖先鏈往上尋找方法,直到找到這個方法或者拋出異常。
以在TLC中調用puts方法為例,此時puts方法的接收者隱式由self充當,而此時的self是Object類的對象main,那麼查看Object類的方法:
irb(main):041:0> Object.methods.grep /puts/=> []
在Object中顯然並不存在puts方法,那麼查看Object的祖先鏈:
irb(main):042:0> Object.ancestors=> [Object, Kernel, BasicObject]
沿著祖先鏈,我們查看Kernel的方法:
irb(main):043:0> Kernel.methods.grep /puts/=> [:puts]
顯然puts方法位於Kernel中。從前面知道,Kernel是一個模組,它被混入(Mixin)類中,通常當模組include混入時,模組的方法就成為類的執行個體方法,由於Kernel模組混入了Object類中,所以在ruby代碼的任何地方都可以調用puts方法,因為ruby的幾乎全部的類都繼承自Object。
PS.關於private方法:
ruby中的private方法有時讓人很懊惱。但實際上需要記住的只有一點:private方法只能在隱含的接收者self上被調用,但private方法調用的尋找規則和其他方法是一樣的。所以,即便是TLC中main對象擁有puts方法,但卻不能這樣調用:
irb(main):046:0> self.putsNoMethodError: private method `puts' called for main:Object from (irb):46 from C:/RailsInstaller/Ruby1.9.3/bin/irb:12:in `<main>'
另外,不要和Java等語言中的private混淆,ruby的private方法時可以在子類中調用的:
irb(main):052:0> class Dadirb(main):053:1> privateirb(main):054:1> def sayirb(main):055:2> puts "Dad says private"irb(main):056:2> endirb(main):057:1> end=> nilirb(main):058:0> class Son <Dadirb(main):059:1> def talkirb(main):060:2> sayirb(main):061:2> puts "son talk"irb(main):062:2> endirb(main):063:1> end=> nilirb(main):064:0> s=Son.new=> #<Son:0x1c90cb0>irb(main):065:0> s.talkDad says privateson talk
最後,補充說一下幾個常用方法的調用:
irb(main):067:0> Object.private_methods.grep /method/=> [:define_method, :method_missing,.........]
Objcet的method_missing和define_method都是其私人方法,為什麼在方法內部調用method_missing正確,而調用define_method就會報錯,必須在類上下文充當self時才能調用define_method?
Object#method_missing方法是Object的一個private方法,不能顯示地使用接收者調用,但所有對象都可以隱式調用該方法。
而define_method實際上是Module#define_method方法,該方法是Module的private執行個體方法,而Class又繼承了Module類,所以它變成了Class的執行個體方法,由於所有的普通類都是Class的執行個體,所以define_method就成為了所有普通類的類方法,所以在類上下文中能夠使用define_method此時self由類充當,而不能在某個具體對象充當self時調用該方法。