ruby的class << self, 及其class_eval和instance_eval的區別

昨天在看web敏捷開發之道的時候看到class << self這樣的語法,一會兒弄蒙了,丫的這是什麼語法阿。呆了,徹底看不懂。後來經查google才知道是單態方法——singleton_method java


關於class << self,有一篇比較好的博文,請見:http://www.devalot.com/articles/2008/09/ruby-singleton web


定義singleton_methods有三種方式 編程


第一種: 直接給對象打開定義 ruby

這是最簡單相信也是見得最多的一種方式: 函數

[ruby]  view plain copy
  1. a = Array.new  
  2.   
  3. def a.size  
  4.   "Hello World!"  
  5. end  

輸入puts  a.size  ====>輸出"Hello,World",而不是0


咱們知道ruby比較有特色的一個地方是,在任什麼時候間任何地點,能夠對一個類或對象打開進行添加方法或者重寫方法。 post

但這是爲何呢?不知道讀者有沒有想過這個問題。從靜態語言的角度,好比java或者C++,我麼知道只能在一個類的範圍內添加方法。學過彙編的朋友知道,方法自己是內存中的一段指令而已。只不過把一些傳參或者返回之放在指定的寄存器上。而ruby底層是用C寫的。C++和java中的類本質跟C中的結構體也沒有什麼區別,術語多態和回調只不過是在指定的方法表(每一個java類字節碼都有一個方法表)入口處放置實際C的函數指針。 google


通過上面的分析,是否是意味着ruby類的開放性也是基於類的呢? spa

查過google以後,發覺分析對了。原來ruby在任何地方能對一個既存的對象打開也是基於類的,只不過是個匿名的內部類而已,而這個類繼承於當前實例的類。 .net

因此,在上面的代碼中,咱們徹底能夠調用super方法調用父類的相應函數。如 指針

[ruby]  view plain copy
  1. a = Array.new  
  2.   
  3. def a.size  
  4.   puts super  
  5.  "Hello World!"  
  6. end  

puts a.size ======> 輸出

0

"Hello,World"

說明咱們理解沒有誤差。


這樣,咱們對於第二種方式也就不難理解了。


第二種:使用"<<"方式


上面的代碼還能夠這樣寫:

[ruby]  view plain copy
  1. a = Array.new  
  2.   
  3.   
  4. class << a  
  5.   def size  
  6.     puts super  
  7.     "Hello,World"  
  8.   end  
  9. end  
  10.   
  11. puts a.size =======>   
  12. 0  
  13. "Hello,World"  
  14.   
  15. b = Array.new  
  16. puts b.size =========>  
  17. 0  

很顯然,第二種方式比較能體現本質(某一個類繼承於a,而後重寫size這個方法)


第三種方式,經過instance_eval

[ruby]  view plain copy
  1. a = Array.new  
  2.   
  3. a.instance_eval <<EOF  
  4.   def size  
  5.     puts super  
  6.     puts "Hello,World!"  
  7.   end  
  8. EOF  
  9.   
  10. <strong>b = Array.new  
  11.   
  12.   
  13. a.size =======>   
  14.   
  15. 0  
  16.   
  17. "Hello,World"  
  18.   
  19. b.size ====>  
  20.   
  21. 0</strong>   

雖然明白instance_eval是singleton_method方式的一種,可是由於是第一次見instance_eval,因而google之,發現原來這玩意兒和class_eval很類似。因而又google class_eval.


因而,又發現一篇比較好的博文:http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval

博文中說道,class_module是從Module中獲得的,因此class_module的接受者必須是個類,好比說

[ruby]  view plain copy
  1. a=Array.new  
  2. a.class_eval 這樣是錯的,會報找不到class_eval這個方法  
  3.   
  4. 應該是Array.class_eval才能夠  
而咱們知道在混合編程(mix)中,include 某個Module時,該Module的全部實例方法只能由子類的實例所調用(至關於多了繼承於Module),故class_eval定義以後,只能是Array的全部的實例調用,Array本身調用會出錯(確定的,假如Array本身調用不出錯的話,這個方法就是靜態方法了)


[ruby]  view plain copy
  1. a=Array.new  
  2.   
  3. Array.class_eval do  
  4.   def hint  
  5.     "hello"  
  6.   end  
  7. end  
  8.   
  9. puts a,hint  輸出hello  
  10. b = Array.new  
  11. puts b.hint  輸出hello  
  12.   
  13. puts A.hint 出錯,沒有找到這個方法  

ruby是徹底面向對象的,即便對於某個實際的類也是如此。ruby的任何類,好比Array,或者是任何本身定義的類,都是Class類的實例。因此,類的靜態方法,至關於這個類(或者是Class類的對象)的singleton_methods.也就是說,ruby本質是沒有靜態方法的。


基於此,也就很天然的明白instance_eval了。

好比,我像下面這樣定義

[ruby]  view plain copy
  1. a=Array.new  
  2. a.instance_eval do  
  3.   def hint  
  4.    "hello"  
  5.   end  
  6. end  
  7.   
  8. puts a.hint  輸出hello  
  9. b=Array.new  
  10. puts b.hint  報錯,沒有找到此方法  
  11.   
  12. 假如我像下面這樣定義:  
  13. Array.instance_eval do  
  14.   def hint  
  15.    "hello"  
  16.   end  
  17. end  
  18.   
  19. 本質上至關於這樣的代碼:  
  20. class Array  
  21.   def self.hint  
  22.     "hello"  
  23.   end  
  24. end  
  25.   
  26. 固然,也能夠是這樣  
  27. class Array  
  28.   class << self  
  29.     def hint  
  30.       "hello"  
  31.     end  
  32.   end  
  33. end  
  34.   
  35. <strong><span style="color:#660000;">不過ruby對靜態代碼的限制比較強,不像java,實例能夠調用靜態方法,在ruby中,靜態方法只能是類調用,由於ruby本質是沒有靜態方法的,只有實例方法。只有所屬的對象才能調用本身的  
  36. 方法,這也間接地說明了ruby是一種徹底的面嚮對象語言。</span></strong>  
相關文章
相關標籤/搜索