Ruby中的类变量,很多文章都是不太建议使用的,主要原因在于他的一些特性容易导致犯一些错误,尤其在广泛使用元编程的时候。
初步接触类变量可能觉得他跟C++的类静态成员和Java中的静态变量没什么区别,但在实际使用中一不留神就会掉到类变量的陷阱中去
陷阱1,类变量跟类实例变量不同,类实例变量关联的是self,但类变量关联的是当前类作用域
class C end class D end class C @@var = "C" def D.getvar @@var end end class D @@var = "D" def C.getvar @@var end end C.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" end end class D < C @@var2 = "D_var2" def getvar @@var2 end end c = C.new d = D.new p c.getvar #"C_var1" p d.getvar #"D_var2" c.setvar p c.getvar #"C_var1" p d.getvar #"C_var2"
可以看到,子类和超类有各自的同名实例函数,但是却共享一个相同的类变量名。
陷阱3,跟陷阱2类似,Object中如果定义一个类变量,会将所有类中的同名类变量污染
同样的,如果两个不同的子类中有两个相同的类变量,这本来是不干扰的,但是一旦他们共同的超类定义了该类变量,这三个类变量就会强制统一!!
而我们知道类共同的超类是Object,那么一旦你在Object中定义了一个类变量,就会导致所有的类的同名类变量都受到污染!!
class C @@var = "C_var" def getvar @@var end end class D @@var = "D_var" def getvar @@var end end c = C.new d = D.new p 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中拿掉。