Разница между переменными класса и переменными экземпляра класса?

Может ли кто-нибудь рассказать мне о различии между переменными класса и переменными экземпляра класса?

Ответ 1

Переменная класса (@@) разделяется между классом и всеми его потомками. Переменная экземпляра класса (@) не разделяется потомками класса.


Переменная класса (@@)

Пусть имеется класс Foo с переменной класса @@i и аксессуар для чтения и записи @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

И производный класс:

class Bar < Foo
end

Мы видим, что Foo и Bar имеют одинаковое значение для @@i:

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

И меняя @@i на один, он меняет его в обоих:

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

Переменная экземпляра класса (@)

Сделайте простой класс с переменной экземпляра класса @i и аксессуарами для чтения и записи @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

И производный класс:

class Bar < Foo
end

Мы видим, что хотя Bar наследует аксессоров для @i, он не наследует @i сам:

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

Мы можем установить Bar @i, не затрагивая Foo @i:

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

Ответ 2

Сначала вы должны понимать, что классы тоже экземпляры - экземпляры класса Class.

Как только вы это понимаете, вы можете понять, что класс может иметь связанные с ним переменные экземпляра, как может быть обычный объект (read: non-class).

Hello = Class.new

# setting an instance variable on the Hello class
Hello.instance_variable_set(:@var, "good morning!")

# getting an instance variable on the Hello class
Hello.instance_variable_get(:@var) #=> "good morning!"

Обратите внимание, что переменная экземпляра на Hello полностью не связана и не отличается от переменной экземпляра в экземпляре Hello

hello = Hello.new

# setting an instance variable on an instance of Hello
hello.instance_variable_set(:@var, :"bad evening!")

# getting an instance variable on an instance of Hello
hello.instance_variable_get(:@var) #=> "bad evening!")

# see that it distinct from @var on Hello
Hello.instance_variable_get(:@var) #=> "good morning!"

A переменная класса, с другой стороны, представляет собой комбинацию двух вышеперечисленных, поскольку она доступна для самого Hello и ее экземпляров, а также для подклассов Hello и их экземпляры:

HelloChild = Class.new(Hello)
Hello.class_variable_set(:@@class_var, "strange day!")
hello = Hello.new
hello_child = HelloChild.new

Hello.class_variable_get(:@@class_var) #=> "strange day!"
HelloChild.class_variable_get(:@@class_var) #=> "strange day!"
hello.singleton_class.class_variable_get(:@@class_var) #=> "strange day!"
hello_child.singleton_class.class_variable_get(:@@class_Var) #=> "strange day!"

Многие говорят, что избегают class variables из-за странного поведения выше и рекомендуют вместо этого использовать class instance variables.