Search code examples
rubyinstance-variablesclass-variablesclass-instance-variables

Ruby class instance variable vs. class variable


I read When do Ruby instance variables get set? but I'm of two minds when to use class instance variables.

Class variables are shared by all objects of a class, Instance variables belong to one object. There's not much room left to use class instance variables if we have class variables.

Could someone explain the difference between these two and when to use them?

Here's a code example:

class S
  @@k = 23
  @s = 15
  def self.s
    @s
  end
  def self.k
     @@k
  end

end
p S.s #15
p S.k #23

Update: I understand now! Class Instance Variables are not passed along the inheritance chain.


Solution

  • 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]