クラス変数とインスタンス変数

職場で「クラス変数とクラスインスタンス変数の違い」が話題にあがっていて、以前理解したよな、と思いつつ咄嗟に整理して述べられないなと思ったので、思い出しつつまとめてみる。

基本的にはクラス変数とインスタンス変数というものがある。

まずサンプルコード。

class Parent
  @@var = 'class variable of Parent'
  @var = 'instance variable of Parent (so called class instance variable)'

  class << self
    def at_at_var
      @@var
    end

    def at_var
      @var
    end
  end

  def initialize
    @var = 'instance variable of parent'
  end

  def at_at_var
    @@var
  end

  def at_var
    @var
  end
end

インスタンス変数とは

あるインスタンスが保持する変数。そのインスタンスにおいて利用できる。@{変数名}により指示される。

たとえば、parentという「Parentクラスのインスタンス」は@var(='instance variable')というインスタンス変数をもっている。

parent = Parent.new
parent.at_var  # => "instance variable of parent"

同様に、Parentという「Classクラスのインスタンス」は@var(='instance variable of Parent (so called class instance variable)')というインスタンス変数をもっている。

Parent.at_var  # => "instance variable of Parent (so called class instance variable)"

後者のように「Classクラスのインスタンス」が持つインスタンス変数のことを「クラスインスタンス変数」と呼ぶことがある。

クラス変数とは

あるクラスが保持する変数。@@{変数名}により指示される。

当然ながらParentクラスは自身のクラス変数を利用できる。

Parent.at_at_var  # => "class variable of Parent"

クラス変数はこれに加えて、以下の特徴をもつ。

1. Parentクラスのインスタンスから、Parentクラスのクラス変数にアクセスできる
parent.at_at_var  # => "class variable of Parent"
2. Parentクラスを継承したChildクラスから、継承元であるParentクラスのクラス変数にアクセスできる
class Child < Parent
  def initialize
    @var = 'instance variable of child'
  end
end
Child.at_at_var  # => "class variable of Parent"
3. (上記の合わせ技として)Parenetクラスを継承したChildクラスのインスタンスから、Childクラスの継承元であるParentのクラス変数にアクセスできる
child = Child.new
child.at_at_var  # => "class variable of Parent"

おまけ

仕組みはこれだけだと思う。最後に念のため、上で述べたクラス変数の「色んなところからアクセスできる性質」は、クラスインスタンス変数には与えられていないことを確認する。

1. Parentクラスのインスタンスから、Parentクラスのクラスインスタンス変数にはアクセスできない
parent.at_var  # => "instance variable of parent"

@varはparentのインスタンス変数を指してしまっている。

2. Parentクラスを継承したChildクラスから、継承元であるParentクラスのクラスインスタンス変数にはアクセスできない
Child.at_var  # => nil

@varは「ChildというClassクラスのインスタンスが持つインスタンス変数」を指してしまっている。(これは宣言していないのでnilが返っている。)

3. Parenetクラスを継承したChildクラスのインスタンスから、Childクラスの継承元であるParentのクラスインスタンス変数にはアクセスできない
child.at_var  # => "instance variable of child"

@varはchildのインスタンス変数を指してしまっている。

丁寧に言語化すると「ClassクラスのインスタンスであるHogeクラスのクラスインスタンス変数」とか、どうしても早口言葉っぽくなるな...。

以上です。