ruby中的方法查找

  ruby中的方法調用都是 對象.方法 的形式,那麼對象如何找到這個方法呢?ruby

  首先必須瞭解祖先鏈的概念,祖先鏈就是從一個類開始,到它的父類,再到父類的父類...一直到最終的起點(ruby中是BasicObject類)。這期間經歷過的路徑就是祖先鏈。函數

  1混含模塊和繼承的方法查找this

  對於一個實例對象,先找它屬於的類中是否有對應的實例方法,而後看這個類中是否有模塊,若是有,查找模塊中是否有對應的方法,若是沒有,則查找父類。先看父類的實例方法,再看父類中是否有模塊,再看父類的父類..一直到最後,BasicObject類和Kernel模塊。spa

  若是尚未,則會去查看method_missing函數,這個函數是內建函數。這個函數默認是報錯,固然你也能夠重寫這個方法,來對沒有找到的方法在其中進行處理。code

以下:對象

 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 end
10 
11 class D < C;end
12 
13 D.new.method
14 p D.ancestors

輸出是blog

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中的全部對象均可以用這個方法。class

 

  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     end
11 end
12 
13 class    C
14     include M
15     include N
16 end
17 
18 C.new.method
19 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.new
11 class << c
12     def method
13         puts "this is method in c' singleton class"
14     end
15     include M
16     p ancestors
17 end
18 
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 ancestors
10 end
11 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 << c
10     include M
11     p ancestors
12 end
13 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;end
11 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

相關文章
相關標籤/搜索