Я забыл, что в Ruby существует концепция «переменная экземпляра класса». В любом случае, проблема ОП казалась загадочной, и до сих пор ее не решали ни в одном из ответов, за исключением подсказки в ответе КЧ: это проблема охвата. (Добавлено при редактировании: на самом деле, ответ sris действительно решает эту проблему в конце, но я все равно оставлю этот ответ в силе, так как думаю, что пример кода может быть полезен для понимания проблемы.)
В классе Ruby имя переменной, начинающееся с @
, может ссылаться на одну из двух переменных: либо на переменную экземпляра , либо на переменную экземпляра класса , в зависимости от того, где в классе он упоминается. Это довольно тонкий вопрос.
Пример прояснит суть. Вот небольшой тестовый класс Ruby (весь код протестирован в irb):
class T
@@class_variable = "BBQ"
@class_instance_variable_1 = "WTF"
@class_instance_variable_2 = "LOL"
def self.class_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def initialize
@instance_variable = "omg"
# The following line does not assign a value to the class instance variable,
# but actually declares an instance variable withthe same name!
@class_instance_variable_1 = "wtf"
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def instance_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
end
Я назвал переменные в соответствии с тем, что, как я думал, они были, хотя оказывается, что не всегда так:
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
irb> t = T.new
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">
irb> t.instance_method
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> nil
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
@@class_variable
и @instance_variable
всегда ведут себя так, как и следовало ожидать: первый определяется на уровне класса, и независимо от того, упоминается ли он в методе класса или в методе экземпляра, он содержит значение, присвоенное ему на Топ. Последний получает значение только в объекте класса T
, поэтому в методе класса он ссылается на неизвестную переменную со значением nil
.
Метод класса с художественным именем с именем class_method
выводит значения @@class_variable
и два @class_instance_variable
с, как и ожидалось, то есть инициализируется в верхней части класса. Однако в методах экземпляра initialize
и instance_method
, различные переменные с одинаковым именем доступны, то есть переменные экземпляра, а не переменные экземпляра класса .
Вы можете видеть, что присвоение в методе initialize
не повлияло на переменную экземпляра класса @class_instance_variable_1
, потому что более поздний вызов class_method
выводит его старое значение, "WTF"
. Вместо этого метод initialize
объявил новую переменную экземпляра, , которая также названа (обманчиво) @class_instance_variable_1
. Присвоенное ему значение "wtf"
выводится методами initialize
и instance_method
.
Переменная @class_instance_variable_2
в примере кода эквивалентна переменной @hello
в исходной задаче: она объявлена и инициализирована как переменная экземпляра класса, но когда метод экземпляра ссылается на переменную с таким именем, он фактически видит переменная экземпляра с тем же именем - переменная, которая никогда не была объявлена, поэтому ее значение равно nil.