In Ruby, the double at-sign (@@
) before a variable name (e.g. @@variable_name
) is used to create a class variable. These variables are:
- Static — i.e. only a single copy of the variable exists, regardless of however many instances of the class you create;
- Globally scoped within the context of inheritance hierarchy — i.e. they're shared between a class and all its subclasses.
For example, consider the following class where a counter
is incremented each time a new object is instantiated:
class Entity @@counter = 0 def initialize @@counter += 1 puts "There are #{@@counter} entities" end end a = Entity.new b = Entity.new c = Entity.new # output: # "There are 1 entities" # "There are 2 entities" # "There are 3 entities"
Class variables can be problematic though; for example, a class variable might (accidentally) be reassigned by any of its subclasses, affecting all other classes:
class Parent @@counter = 0 def initialize @@counter += 1 end def self.counter @@counter end end class Child < Parent @@counter = 100 end a = Parent.new b = Parent.new c = Parent.new d = Child.new puts Parent.counter # output: 104
The output here is 104
because the Child
class sets the initial value of @@counter
to 100
. After that there are a total of four object instances that are created. This results in @@counter
being incremented four times, updating the @@counter
value to 104
. If that sounds confusing, then perhaps the following example will give you a clearer understanding of this:
class Parent @@name = "parent" def self.name @@name end end puts Parent.name # output: "parent" class Child < Parent @@name = "child" end puts Parent.name # output: "child"
Similarly, when/if an ancestor class changes the value of the class variable, you would run into the same issue:
class Parent @@name = "parent" def self.name @@name end end puts Parent.name # output: "parent" class Object @@name = "object" end puts Parent.name # output: "object"
As you can see, basically any sub-class or ancestor class that is defined after the parent could potentially reassign a class variable leading to unexpected behavior:
class Parent @@name = "parent" def self.name @@name end end puts Parent.name # output: "parent" class Child < Parent @@name = "child" end puts Parent.name # output: "child" class Object @@name = "object" end puts Parent.name # output: "object"
Therefore, you should generally avoid class variables, and use instance variables instead.
Hope you found this post useful. It was published . Please show your love and support by sharing this post.