我想擴大在其他人所說,特別是,與類的實例變量比較使用類變量。讓我們先從這一點:
class Holder1
end
class Holder2 < Holder1
@@var = 99
# Create a class setter and a getter for the class variable
def Holder2.var=(val)
@@var = val
end
def Holder2.var
@@var
end
# Create a instance getter for the class variable
def var
@@var
end
end
class Holder3 < Holder2
end
Holder2.var #=> 99
Holder2.var = 1
Holder3.var #=> 1
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder2.new.var #=> 1
Holder3.new.var #=> 1
Holder3.var = 2
Holder3.var #=> 2
Holder2.var #=> 2
Holder3.new.var #=> 2
Holder2.new.var #=> 2
旁白:前兩種方法通常會被寫成這樣:
def self.var=(val)
@@var = val
end
def self.var
@@var
end
這工作,因爲self
=>Holder2
在創建方法時。如果您決定重命名班級,則不需要修改班級名稱,也不需要進行更改。
現在這個(使用類變量)可能正是你想要的行爲。也就是說,如果您希望子類查看變量並能夠更改它,則需要一個類變量。
但是,如果你想每個子類有自己的變量,它可以既看不見也由子類改變,你需要使用一個類實例變量,@var
,而不是一類變量,@@var
。 (從技術上講,這是不正確的,因爲你可以在你的程序的任何地方使用Holder2.instance_variable_get(:@var)
或Holder2.instance_variable_set(:@var)
)。
比較下面的代碼和上面的代碼的結果。我已經包含一個實例變量,其名稱與類實例變量@var
相同,以說明它們與@night
和@day
不同。
class Holder1
end
class Holder2 < Holder1
# Create an accessor for the instance variable
attr_accessor :var
# Initialize class instance variable
@var = 99
# Create an accessor for the class instance variable
class << self
puts "self in 'class << self': #{self}"
attr_accessor :var
def our_dog
"Diva"
end
end
# Create a class method
def self.our_cat
puts "self in 'self.our_cat())' def: #{self}"
"Teagan"
end
# Create an instance setter and a getter for the class instance variable
def c_ivar=(val)
self.class.var = val
end
def c_ivar
self.class.var
end
end
class Holder3 < Holder2
end
#=> self in 'class << self': #<Class:Holder2>
Holder2.var #=> 99
h2 = Holder2.new
h2.var #=> nil
Holder2.var = 1
Holder2.var #=> 1
h2.c_ivar #=> 1
h2.c_ivar = 2
Holder2.var #=> 2
h2.var #=> nil
h2.var = 3
h2.var #=> 3
Holder2.var #=> 2
Holder3.var #=> nil
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder3.var = 4
Holder3.var #=> 4
Holder2.var #=> 2
h3 = Holder3.new
h3.c_ivar #=> 4
h2.c_ivar #=> 2
Holder3.our_dog #=> "Diva"
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3"
#=> "Teagan"
Holder1.var
拋出一個異常,因爲這個類有Holder2
的類實例變量[email protected]
進不去。相比之下,Holder3.var
的第一次使用返回nil
,因爲變量存在Holder3
,通過繼承,但尚未初始化。
表達class << self
改變從Holder2
self
到Holder2
的單例類(又名元類),直到下一個end
。請注意,Ruby表示Holder2
的單例類爲#<Class:Holder2>
。另外,我們不需要在self.
前面加上my_dog
,因爲在創建方法時,self
是Holder2
的單例類。
注意:
Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat]
這表明our_cat()
是Holder2
的方法的單例類,即使self
是Holder2
(而不是Holder2
的單例類',#<Class:Holder2>
)構建的方法時。出於這個原因,有人說,在技術上,"there is no such thing as a class method"。這也告訴我們,我們可以將my_cat
的定義移到class << self
構造中(並丟棄self.
)。
另一種常見的方式來實例變量和方法添加到Holder2
的單例類是extend M
更換class << self ... end
並創建一個模塊:
module M
attr_accessor :var
def our_dog
"Diva"
end
end
Object#extend混合這些實例變量和方法爲Holder2
的單例類。