標籤:var class 編程 初步 object 子類 使用 原則 環境
Ruby中的類變數,很多文章都是不太建議使用的,主要原因在於他的一些特性容易導致犯一些錯誤,尤其在廣泛使用元編程的時候。
初步接觸類變數可能覺得他跟C++的類靜態成員和Java中的靜態變數沒什麼區別,但在實際使用中一不留神就會掉到類變數的陷阱中去
陷阱1,類變數跟類執行個體變數不同,類執行個體變數關聯的是self,但類變數關聯的是當前類範圍
class Cendclass Dendclass C @@var = "C" def D.getvar @@var endendclass D @@var = "D" def C.getvar @@var endendC.getvar#=> "D"D.getvar#=> "C"
在這個例子裡面我在類C裡面定義D的方法,在D裡面定義C的方法,結果C的方法返回了D的類變數,而D的方法返回了C的類變數,這就是由於類變數跟當前環境的self無關,只跟所在環境的類範圍有關,如果不包含在class關鍵字中,則視為在Object中。
是不是感覺有點怪怪的啊,不過更怪的還在後面。
陷阱2,超類如果增加一個子類已有的同名類變數,會將子類的類變數汙染
我們知道類的繼承帶有引用性質,在一個引用樹上,子類可以重寫超類的方法和變數而不對超類產生影響,但對於類變數這個事情不成立
類變數在整個繼承樹中只有一個實體,首先超類中如果有類變數,子類完全繼承,而子類中的類變數原則上是不影響超類的,但一旦超類中加入了相同的類變數就會自動把子類的類變數重寫!!
class C @@var1 = "C_var1" def getvar @@var1 end def setvar @@var2 = "C_var2" endendclass D < C @@var2 = "D_var2" def getvar @@var2 endendc = C.newd = D.newp c.getvar#"C_var1"p d.getvar#"D_var2"c.setvarp c.getvar#"C_var1"p d.getvar#"C_var2"
可以看到,子類和超類有各自的同名執行個體函數,但是卻共用一個相同的類變數名。
陷阱3,跟陷阱2類似,Object中如果定義一個類變數,會將所有類中的同名類變數汙染
同樣的,如果兩個不同的子類中有兩個相同的類變數,這本來是不干擾的,但是一旦他們共同的超類定義了該類變數,這三個類變數就會強制統一!!
而我們知道類共同的超類是Object,那麼一旦你在Object中定義了一個類變數,就會導致所有的類的同名類變數都受到汙染!!
class C @@var = "C_var" def getvar @@var endendclass D @@var = "D_var" def getvar @@var endendc = C.newd = D.newp c.getvar# "C_var"p d.getvar# "D_var"class Object @@var = "Object_var"end# @@var = "main_var"# 使用這句有同樣的效果,不過會得到一個警告p c.getvar# "Object_var"p d.getvar# "Object_var"
類變數在一個繼承樹中只能有一個,不允許重寫,但在不同的繼承樹中卻允許重名,這個奇葩的東西還是少碰點的好
因此慎用類變數,尤其是不要用猴子補丁來定義類變數,我甚至覺得這個奇怪的東西應該從Ruby中拿掉。
談談Ruby中的類變數