Ruby: Class Instance Variables VS Instance Variables

Ruby class instance variable vs. class variable

Instance variable on a class:

class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end

class Child < Parent
@things = []
end

Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]

Class variable:

class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end

class Child < Parent
end

Parent.things << :car
Child.things << :doll

p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

With an instance variable on a class (not on an instance of that class) you can store something common to that class without having sub-classes automatically also get them (and vice-versa). With class variables, you have the convenience of not having to write self.class from an instance object, and (when desirable) you also get automatic sharing throughout the class hierarchy.


Merging these together into a single example that also covers instance variables on instances:

class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class

def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end

attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end

class Child < Parent
@shared_things = []
end

And then in action:

mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]

p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]

Difference between class variables and class instance variables?

A class variable (@@) is shared among the class and all of its descendants. A class instance variable (@) is not shared by the class's descendants.


Class variable (@@)

Let's have a class Foo with a class variable @@i, and accessors for reading and writing @@i:

class Foo

@@i = 1

def self.i
@@i
end

def self.i=(value)
@@i = value
end

end

And a derived class:

class Bar < Foo
end

We see that Foo and Bar have the same value for @@i:

p Foo.i    # => 1
p Bar.i # => 1

And changing @@i in one changes it in both:

Bar.i = 2
p Foo.i # => 2
p Bar.i # => 2

Class instance variable (@)

Let's make a simple class with a class instance variable @i and accessors for reading and writing @i:

class Foo

@i = 1

def self.i
@i
end

def self.i=(value)
@i = value
end

end

And a derived class:

class Bar < Foo
end

We see that although Bar inherits the accessors for @i, it does not inherit @i itself:

p Foo.i    # => 1
p Bar.i # => nil

We can set Bar's @i without affecting Foo's @i:

Bar.i = 2
p Foo.i # => 1
p Bar.i # => 2

Difference between Ruby's class variable and instance variable

I think "instance variable, class variable and the difference between them in ruby" has a good explanation of the difference between local, instance and class variables.

Normal Variables Vs Instance variable in Ruby, Whats the difference?

A normal variable has scope only within the current context; an instance variable has scope throughout one instance of a class. In your case they're confused because the context is main, which acts as an instance of Object.

Consider the following, which may make things clearer

class User
def set_name
@name = "Bob"
surname = "Cratchett"
end

def hi
puts "Hello, " + @name
end

def hello
puts "Hello, Mr " + surname
end
end

irb(main):022:0> u = User.new
=> #<User:0x29cbfb0>
irb(main):023:0> u.set_name
irb(main):024:0> u.hi
Hello, Bob
=> nil
irb(main):025:0> u.hello
NameError: undefined local variable or method `surname' for #<User:0x29cbfb0 @name="Bob">

difference between class method , instance method , instance variable , class variable?

First take a look at this diagram:

from "Metaprogramming Ruby" book

You can rightly say that “obj has a method called my_method( ),” meaning that you’re able to call obj.my_method(). By contrast, you shouldn’t say that “MyClass has a method named my_method().” That would be confusing, because it would imply that you’re able to call MyClass.my_method() as if it were a class method.

To remove the ambiguity, you should say that my_method() is an instance method (not just “a method”) of MyClass, meaning that it’s defined in MyClass, and you actually need an instance of MyClass to call it. It’s the same method, but when you talk about the class, you call it an instance method, and when you talk about the object, you simply call it a method. Remember this distinction, and you won’t get confused when writing introspective code like this:

String.instance_methods == "abc".methods # => true String.methods == "abc".methods # => false

an object’s instance variables live in the object itself, and an object’s methods live in the object’s class. That’s why objects of the same class share methods but don’t share instance variables.

Constants vs instance variable

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!"


Related Topics



Leave a reply



Submit