Reputation: 209
When would you use a constant as opposed to a class instance variable? They both have the same scope. Examples would help.
Upvotes: 0
Views: 3753
Reputation: 48589
About a constant and an instance variable having the same scope:
C = 10
class Dog
def initialize
puts C #=>10
@x = 1
puts @x #=>1
end
end
d = Dog.new
puts C #=>10
p @x #=>nil It sure doesn't look like @x has the same scope as C.
...
@x = 1
class Dog
C = 10
def initialize
puts C #=>10
p @x #=>nil Here @x does not have the same scope as C.
end
end
d = Dog.new
puts @x #=>1
puts C #=>Error uninitialized constant. Here C does not have the same scope as @x.
Now for some jargon:
An instance variable attaches itself to whatever object is self at the time the instance variable is created.
An instance variable is looked up in whatever object is self at the time the instance variables is summoned.
Inside initialize(), a Dog instance is self. At the toplevel, self is an Object called 'main'. That should allow you to figure out the results above.
@variables attach themselves to instances, while methods attach themselves to a Class. Instances of the same class share the methods in the class, but instances of a class do not share @variables--each instance has its own @variables; two instances don't even have to have the same @variables:
class Dog
attr_accessor :x, :y, :z
def initialize
end
end
d1 = Dog.new
d1.x = 10
d2 = Dog.new
d2.y = 1
d2.z = 2
p d1.instance_variables
p d2.instance_variables
--output:--
[:@x]
[:@y, :@z]
Local variables, e.g. 'x', 'y', are for storing values that can change.
Constants are for storing values that you don't want to change.
@variables are for attaching values to an instance. If you want an @variable to be constant, then don't define a setter method, although that is not foolproof because ruby permits a programmer to violate privacy if they wish.
Upvotes: 0
Reputation: 12402
Consider this class:
class Dog
NUMBER_OF_LEGS = 4
@dog_counter = 0
attr_reader :name
def initialize(name)
@name = name
end
def legs
NUMBER_OF_LEGS
end
end
Here, NUMBER_OF_LEGS
is a constant, @name
is an instance variable (with a getter method), and @dog_counter
is what's called a class instance variable.
In Ruby everything is an object, even classes, and as such they can have their own instance variables.
Look at how we could use this class:
dog = Dog.new('Chewbacca')
dog.name
# => 'Chewbacca'
dog.legs
# => 4
Dog::NUMBER_OF_LEGS
# => 4
That's fine, but we do not have a direct interface to access @dog_counter
. The only way to do something with it is to use introspection methods:
dog.class.instance_variable_get(:@dog_counter)
# => 0
dog.class.instance_variable_set(:@dog_counter, 1)
dog.class.instance_variable_get(:@dog_counter)
# => 1
dog.class.instance_eval { @dog_counter = 10 }
dog.class.instance_variable_get(:@dog_counter)
# => 10
We can do better than that. Look at this other implementation:
class Dog
@dog_counter = 0
attr_reader :name
class << self
attr_accessor :dog_counter
end
def initialize(name)
@name = name
self.class.dog_counter += 1
end
end
Now we have defined a class accessor (setter and getter), and we're also incrementing it with each new instance. The interface is simple:
Dog.dog_counter
# => 0
dog_1 = Dog.new('Han')
dog_2 = Dog.new('Luke')
Dog.dog_counter
# => 2
dog_2.class.dog_counter
# => 2
As to proper class variables, they are scoped on the class and can be accessed by instances.
The big problem, however, is that they are shared between all classes in the same hierarchy. Each class that sets a new value will update it for all its ancestors and descendants.
For this reason they are generally avoided, and class instance variables are preferred (they are class specific).
class Scientist
@@greet = "Hello, I'm a Scientist!"
def greet
@@greet
end
end
class Biologist < Scientist
@@greet = "Hello, I'm a Biologist!"
end
class Physicist < Scientist
@@greet = "Hello, I'm a Physicist!"
end
class ParticlePhysicist < Physicist
@@greet = "Hello, I'm a ParticlePhysicist!"
end
biologist = Biologist.new
biologist.greet
# => "Hello, I'm a ParticlePhysicist!"
Upvotes: 6
Reputation: 168071
They don't have the same scope. A class and its instances refer to the same constant, but not to the same instance variable, given the same name. Constants can also be referred from the namespace of a module, but instance variables cannot.
When you want to access something that would be referred to both from a class method and an instance method, or from some other module, then you need a constant.
You can go against the warnings, but that is not good. When you have something that changes, it is better to use a variable.
Upvotes: 2