「 Rubyインスタンス変数はいつ設定されますか? 」と読みますが、クラスインスタンス変数を使用するタイミングは2つあります。
クラス変数はクラスのすべてのオブジェクトで共有され、インスタンス変数は1つのオブジェクトに属します。クラス変数がある場合、クラスインスタンス変数を使用する余地はあまりありません。
誰かがこれら2つの違いとそれらをいつ使用するかを説明できますか?
コード例を次に示します。
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
私は今、クラスインスタンス変数が継承チェーンに沿って渡されないことを理解しています!
クラスのインスタンス変数:
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 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]
(そのクラスのインスタンスではなく)クラスのインスタンス変数を使用すると、サブクラスが自動的にサブクラスを取得することなく、そのクラスに共通のものを保存できます(逆も同様です)。クラス変数を使用すると、インスタンスオブジェクトからself.class
を記述する必要がなく、(必要な場合)クラス階層全体で自動的に共有することもできます。
これらを1つの例にマージして、インスタンスのインスタンス変数もカバーします。
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
そして、実際に:
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]
私は主な(唯一の?)違いは継承だと信じています:
class T < S
end
p T.k
=> 23
S.k = 24
p T.k
=> 24
p T.s
=> nil
クラス変数はすべての「クラスインスタンス」(つまりサブクラス)で共有されますが、クラスインスタンス変数はそのクラスのみに固有です。しかし、クラスを拡張するつもりがない場合、違いは純粋にアカデミックです。
#classインスタンス変数はクラスメソッドでのみ使用でき、インスタンスメソッドでは使用できませんが、クラス変数はインスタンスメソッドとクラスメソッドの両方で使用できます。また、クラスインスタンス変数は継承チェーンで失われますが、クラス変数は失われません。
class Vars
@class_ins_var = "class instance variable value" #class instance variable
@@class_var = "class variable value" #class variable
def self.class_method
puts @class_ins_var
puts @@class_var
end
def instance_method
puts @class_ins_var
puts @@class_var
end
end
Vars.class_method
puts "see the difference"
obj = Vars.new
obj.instance_method
class VarsChild < Vars
end
VarsChild.class_method
他の人が言ったように、クラス変数は特定のクラスとそのサブクラスの間で共有されます。クラスインスタンス変数は、正確に1つのクラスに属します。そのサブクラスは別個です。
なぜこの動作が存在するのですか?さて、Rubyのすべてはオブジェクト、さらにはクラスです。つまり、各クラスには、対応するクラスClass
(または、Class
のサブクラス)のオブジェクトがあります。 (あなたがclass Foo
と言うとき、あなたは本当に定数Foo
を宣言し、それにクラスオブジェクトを割り当てています。)そして、すべてのRubyオブジェクトはインスタンス変数を持つことができるので、クラスオブジェクトもインスタンス変数を持つことができます。
問題は、クラスオブジェクトのインスタンス変数が、クラス変数の通常の動作とは異なることです。通常、スーパークラスで定義されたクラス変数をそのサブクラスと共有する必要がありますが、それはインスタンス変数の仕組みではありません。サブクラスには独自のクラスオブジェクトがあり、そのクラスオブジェクトには独自のインスタンス変数があります。そこで、彼らはあなたが望んでいる可能性が高い動作を持つ個別のクラス変数を導入しました。
つまり、クラスインスタンス変数は、Rubyの設計の一種の偶然です。探しているものであることが明確にわかっていない限り、おそらく使用しないでください。
クラスインスタンス変数はサブクラス間で共有され、シングルトンメソッドとインスタンスメソッドの両方で参照できるため、クラスインスタンス変数を使用するとすぐに役立つように思えるかもしれませんが、重大な欠点があります。これらは共有されているため、サブクラスはクラスインスタンス変数の値を変更でき、基本クラスも変更の影響を受けます。これは通常、望ましくない動作です。
class C
@@c = 'c'
def self.c_val
@@c
end
end
C.c_val
=> "c"
class D < C
end
D.instance_eval do
def change_c_val
@@c = 'd'
end
end
=> :change_c_val
D.change_c_val
(irb):12: warning: class variable access from toplevel
=> "d"
C.c_val
=> "d"
Railsでは、class_attributeという便利なメソッドが導入されています。名前が示すように、サブクラスが値を継承できるクラスレベルの属性を宣言します。 class_attribute値には、クラスインスタンス変数の場合と同様に、シングルトンメソッドとインスタンスメソッドの両方でアクセスできます。ただし、Railsのclass_attributeの大きな利点は、サブクラスが独自の値を変更でき、親クラスに影響を与えないことです。
class C
class_attribute :c
self.c = 'c'
end
C.c
=> "c"
class D < C
end
D.c = 'd'
=> "d"
C.c
=> "c"
公式Ruby FAQ:クラス変数とクラスインスタンス変数の違いは何ですか?
主な違いは、継承に関する動作です。クラス変数はクラスとそのすべてのサブクラスの間で共有されますが、クラスインスタンス変数は1つの特定のクラスにのみ属します。
クラス変数は、何らかの方法で、継承階層のコンテキスト内のグローバル変数と見なすことができ、グローバル変数に伴うすべての問題があります。たとえば、クラス変数はそのサブクラスのいずれかによって(偶然に)再割り当てされ、他のすべてのクラスに影響を与える可能性があります。
class Woof
@@sound = "woof"
def self.sound
@@sound
end
end
Woof.sound # => "woof"
class LoudWoof < Woof
@@sound = "WOOF"
end
LoudWoof.sound # => "WOOF"
Woof.sound # => "WOOF" (!)
または、先祖クラスが後で再開されて変更される可能性があり、おそらく驚くべき効果があります:
class Foo
@@var = "foo"
def self.var
@@var
end
end
Foo.var # => "foo" (as expected)
class Object
@@var = "object"
end
Foo.var # => "object" (!)
そのため、自分が何をしているかを正確に知り、この種の動作を明示的に必要としない限り、クラスインスタンス変数を使用することをお勧めします。