標籤:style blog color strong 問題 div new size
ruby中的方法調用都是 對象.方法 的形式,那麼對象如何找到這個方法呢?
首先必須瞭解祖先鏈的概念,祖先鏈就是從一個類開始,到它的父類,再到父類的父類...一直到最終的起點(ruby中是BasicObject類)。這期間經曆過的路徑就是祖先鏈。
1混含模組和繼承的方法尋找
對於一個執行個體對象,先找它屬於的類中是否有對應的執行個體方法,然後看這個類中是否有模組,如果有,尋找模組中是否有對應的方法,如果沒有,則尋找父類。先看父類的執行個體方法,再看父類中是否有模組,再看父類的父類..一直到最後,BasicObject類和Kernel模組。
如果還沒有,則會去查看method_missing函數,這個函數是內建函數。這個函數預設是報錯,當然你也可以重寫這個方法,來對沒有找到的方法在其中進行處理。
如下:
1 module M 2 def method 3 puts "this is method in module M" 4 end 5 end 6 7 class C 8 include M 9 end10 11 class D < C;end12 13 D.new.method14 p D.ancestors
輸出是
this is method in module M
[D, C, M, Object, Kernel, BasicObject]
如這個例子,類D的對象D.new要找method方法:先找D中的執行個體方法,沒有,D中也沒有模組。D的父類是C,先找C的執行個體方法,沒有,但C中有模組M,模組M中有method方法,這樣就找到了。這個順序就是按照祖先鏈的順序。
這種尋找能到什麼程度呢?如祖先鏈表示的,接下來就是Object類,這是ruby中一切對象的開始。其中有一個模組Kernel。也就是說,如果你在Kernel中定義了一個方法,那麼ruby中的所有對象都可以用這個方法。
2含有多個相同的方法時的方法尋找
含有多個相同的方法時,會匹配第一個找到的方法。
如下:
1 module M 2 def method 3 puts "this is method in module M" 4 end 5 end 6 7 module N 8 def method 9 puts "this is method in module N"10 end11 end12 13 class C14 include M15 include N16 end17 18 C.new.method19 p C.ancestors
輸出結果
this is method in module N
[C, N, M, Object, Kernel, BasicObject]
類C中包含兩個模組M和N,M和N都有method方法。那麼調用哪個呢?如果在同一個類中,ruby中新定義的方法會覆蓋舊的方法。類似的,模組N相對M是後混如類C的,所以會調用N中的方法。另一個方面,從祖先鏈來看,N也是排在M的前面,因此也是先調用N的方法。
祖先鏈中是模組是怎麼排序的呢?祖先鏈中,一個模組M恰好在包含它的類C中的上一個位置。如這個例子,類C先包含了M,祖先鏈中是C,M。然後又包含了N,N又恰好在C的上一個位置,於是就變成了C,N,M。
3包含單例類的方法尋找
前面的兩種情況是不含單例類的情況,如果含有單例類,就要先考慮單例類了。
單例類:簡單的說就是某個對象特有的類。它只能屬於一個對象(即使是同一個類的其他對象執行個體也不行),因此稱為單例類。
ruby中的每個對象實際上都有兩個類:多個對象執行個體共用的類和單例類。對象調用的方法,就是這兩個類中的執行個體方法,以及祖先類和混含模組中的方法。
有單例類的時候,對象的方法尋找先尋找單例類,然後是單例類混含的模組,然後是對象所屬的類,以此類推。
單例類的父類是對象所屬的類。
如下:
1 module M 2 def method 3 puts "this is method in module M" 4 end 5 end 6 7 class C 8 end 9 10 c = C.new11 class << c12 def method13 puts "this is method in c‘ singleton class"14 end15 include M16 p ancestors17 end18 19 c.method
輸出是
[M, C, Object, Kernel, BasicObject]
this is method in c‘ singleton class
單例類,並沒有在祖先鏈中表示出來,但是調用的方法確實是單例類的方法。然後是混含的模組M,然後是父類,以此類推。從祖先鏈可以看出,單例類的父類是C,是對象c所屬的類。
HELP
在這裡出現了一個問題,假如類C中也包含類別模組M,那祖先鏈理論上說應該是M,C,M,Object,Kernel,BasicObject
如下:
1 module M 2 end 3 class C 4 include M 5 end 6 c = C.new 7 class << c 8 include M 9 p ancestors10 end11 p C.ancestors
輸出結果是:
[C, M, Object, Kernel, BasicObject]
[C, M, Object, Kernel, BasicObject]
單例類裡混含的模組沒有出現在祖先鏈裡,c的單例類和類C的祖先鏈一樣了。
假設類C中包含的不是模組M,而是另一個模組N。
如下:
1 module M 2 end 3 module N 4 end 5 class C 6 include N 7 end 8 c = C.new 9 class << c10 include M11 p ancestors12 end13 p C.ancestors
輸出結果
[M, C, N, Object, Kernel, BasicObject]
[C, N, Object, Kernel, BasicObject]
此時,結果和我預期的一樣。祖先鏈中仍然是有c的單例類混含的模組M的。
這是為什麼呢?難道是說,如果單例類裡和祖先鏈上的其他類混含了同樣的模組,單例類中的模組名字不顯示了?
另外我也在類Object中包含了M,結果是[C, N, Object, M, Kernel, BasicObject],c的單例類中的模組M也沒有。如果是包含N,結果是[M, C, N, Object, N, Kernel, BasicObject],又和預期的一樣。難道是單例類和祖先鏈上的其他類不能包含同樣的模組?
我知道非單例類是可以包含同名的模組的,而且可以同時出現在祖先鏈裡。(我用的是ruby1.9.3)
路過懂得求解答,不勝感激。
4類方法的單例類
上面講的是執行個體對象的單例類。如果是類的單例類呢?(每一個對象都有單例類,類也是對象,當然也有單例類,類方法就是放在單例類裡的。)
單例類不能被繼承,但是單例類是可以有父類或者子類的。
如下:
1 class C 2 def self.method 3 p "This is method in C" 4 end 5 end 6 class D < C 7 end 8 D.method 9 10 en = class << C;self;end11 class E < en;end;
輸出結果
"This is method in C"
can‘t make subclass of singleton class (TypeError)
結果顯示,D.method調用的是C的單例方法,說明D的單例類繼承了C的單例類,是它的子類。但是從10-11行,可知,單例類是不能被繼承的。
我覺得可以這麼認為:在D繼承C的時候,D的單例類繼承了C的單例類,所以D可以調用C的類方法。同理,D也可以調用Object類的類方法。
整理一下:
類的執行個體對象的方法尋找,先找單例類,然後單例類中的模組。再找父類,父類中的模組。以此類推。
類對象的方法尋找,先找單例類(就是類方法),再找父類的單例類,以此類推。
如果找到多個方法,以找到的第一個方法匹配。
ruby中,一個類不能被繼承,它也可以有子類。例如ruby中類的單例類。
如果我們用superclass來找父類的話,可得(#代表單例類,假設d是類D的對象,類D繼承類C,->表示父類是)
#d->D->C->Object->BasicObject->nil
#D->#C->#Object->#BasicObject->Class->Module->Object->BasicObject->nil