Diferença entre variáveis ​​de classe e variáveis ​​de instância de classe?

Respostas:

151

Uma variável de classe ( @@) é compartilhada entre a classe e todos os seus descendentes. Uma variável de instância de classe ( @) não é compartilhada pelos descendentes da classe.


Variável de classe ( @@)

Vamos ter uma classe Foo com uma variável de classe @@ie acessadores para leitura e escrita @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

E uma classe derivada:

class Bar < Foo
end

Vemos que Foo e Bar têm o mesmo valor para @@i:

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

E mudar @@iem um muda em ambos:

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

Variável de instância de classe ( @)

Vamos fazer uma classe simples com uma variável de instância de classe @ie acessadores para leitura e escrita @i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

E uma classe derivada:

class Bar < Foo
end

Vemos que, embora Bar herde os acessadores de @i, ele não herda a @isi mesmo:

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

Podemos definir Bar @isem afetar Foo @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2
Wayne Conrad
fonte
1
Por que retornar variáveis ​​de instância usando métodos de classe? Você costuma se deparar com essa situação?
sekmo
1
@sekmo Os acessadores nesses exemplos retornam variáveis ​​de instância de classe , que pertencem à classe, ou variáveis ​​de classe , que pertencem à hierarquia de classes. Eles não retornam variáveis ​​de instância simples que pertencem a instâncias da classe. Os termos "variável de instância", "varaible de instância de classe" e "variável de classe" são bastante confusos, não são?
Wayne Conrad
72

Primeiro você deve entender que as classes também são instâncias - instâncias da Classclasse.

Depois de entender isso, você pode entender que uma classe pode ter variáveis ​​de instância associadas a ela, assim como um objeto normal (leia-se: não classe) pode.

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

Observe que uma variável de instância em Helloé completamente não relacionada e distinta de uma variável de instância em uma instância deHello

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's distinct from @var on Hello
Hello.instance_variable_get(:@var) #=> "good morning!"

Uma variável de classe, por outro lado, é uma espécie de combinação das duas anteriores, pois é acessível a Hellosi mesma e às suas instâncias, bem como às subclasses de Helloe suas instâncias:

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

Muitas pessoas dizem para evitar class variablespor causa do comportamento estranho acima e recomendam o uso de class instance variables.

cara de cavalo
fonte
2
+1 Super Explicação! Isso é algo que todo programador novo em Ruby deve digerir.
TomZ
0

Também quero acrescentar que você pode obter acesso à variável de classe ( @@) a partir de qualquer instância da classe

class Foo
  def set_name
    @@name = 'Nik'
  end

  def get_name
    @@name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => Nik

Mas você não pode fazer o mesmo para a variável de instância de classe ( @)

class Foo
  def set_name
    @name = 'Nik'
  end

  def get_name
    @name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => nil
Evan Ross
fonte
-1: Incorreto: as variáveis ​​de instância de classe podem ser acessadas de todas as instâncias dessa classe (desde que os acessadores estejam presentes). Além disso, seu exemplo mostra variáveis ​​de instância regulares, não variáveis ​​de instância de classe. Variáveis ​​de instância de classe são declaradas fora dos métodos e são fundamentalmente diferentes de variáveis ​​de instância regulares e variáveis ​​de classe.
The Pellmeister
Como você pode obter acesso às variáveis ​​de instância de classe de cada instância dessa classe?
Evan Ross
Você leu a resposta de Wayne Conrad? Isso literalmente explica tudo. stackoverflow.com/a/3803089/439592
The Pellmeister
Eu sei que você pode obter acesso de métodos de classe como Foo.i, mas como você pode fazer o mesmo para todas as instâncias dessa classe Foo.new.i?
Evan Ross
Usando #class. Eu pessoalmente acho que os #classusos quando chamados em qualquer coisa, mas selfsão cheiros de código embora. Você pode até mesmo dar um passo adiante e implementar acessadores de instância ie i=delegar para seus #classequivalentes, caso em que você pode fazer Foo.new.i. Não recomendo fazer isso, pois cria uma interface confusa, sugerindo que você está modificando um membro do objeto.
The Pellmeister