📘

Rubyのクラス変数とは何か【@@で始まるやつ】

2023/04/07に公開約2,200字

Railsのソースコードを見ていると、@@で定義されたクラス変数を見かけることがある。クラス変数とは、文字通りクラスの中で定義する変数を指す。これは挙動を1つずつ見ていくと理解しやすい。

以下では@@nameというクラス変数を定義している。

class Parent
  @@name = "Denji"

  # クラス変数を返すメソッド
  def self.name
    @@name
  end
end

クラス変数を取得してみる。

# クラス変数の値が返る
p Parent.name #=> "Denji"

コード見たままだが、クラス変数が返る。
では次に、サブクラスを作ってみる。

class Parent
  @@name = "Denji"

  def self.name
    @@name
  end
end

# サブクラスを作成
class Child < Parent
end

この状態で、それぞれのクラスからクラス変数を参照するとどうなるか?

p Parent.name #=> "Denji"
p Child.name #=> "Denji"

Parentクラスを継承したChildクラスで、@@nameクラス変数を参照できているのがわかる。

それでは次に、サブクラス側でクラス変数に値を代入してみるとどうなるか?

class Parent
  @@name = "Denji"

  def self.name
    @@name
  end
end

class Child < Parent
  # サブクラスで値を代入する
  @@name = "Pochita"
end

するとこうなる。

p Parent.name #=> "Pochita" 
p Child.name #=> "Pochita"

サブクラス側で新たな値をクラス変数に代入したところ、親クラス側でも同様の値になっていることがわかる。

では、さらにサブクラスのメソッドでクラス変数に値を代入するとどうなるか?

class Parent
  @@name = "Denji"

  def self.name
    @@name
  end
end

class Child < Parent
  @@name = "Pochita"

  # 値を代入する
  def self.name
    @@name = "Makima"
  end
end

するとこうなる。

p Parent.name #=> "Pochita" 
p Child.name #=> "Makima"

さきほどと同様に、サブクラスの冒頭でクラス変数に新たな値を代入しているため、親クラスではPochitaになっている。
しかし、サブクラスのメソッドではMakimaに変わっている。代入ができている。これは数値にするとわかりやすい。

class Parent
  @@number = 1

  def self.number
    @@number
  end
end

class Child < Parent
  @@number += 1

  def self.number
    @@number += 1
  end
end

p Parent.number #=> 2
p Child.number #=>

サブクラス側の冒頭で@@numberに1を新たに+しているので、Parent.numberが2になっている。
そして、サブクラスのself.numberメソッド内でさらに1を+していて、@@numberは3となっている。

なお、今回のコードではクラスメソッドでクラス変数を参照しているが、インスタンスメソッドからでも参照・代入が可能だ。

class Parent
  @@name = "Denji"

  def self.name
    @@name
  end
end

class Child < Parent
  @@name = "Pochita"

  def name
    @@name = "Makima"
  end
end

p Parent.name #=> "Pochita"

# インスタンスメソッドからでもクラス変数を参照・代入できる
p Child.new.name #=> "Makima"

最後に、変数と定数 (Ruby 3.2 リファレンスマニュアル)にわかりやすい解説があるので引用させていただく。

クラス変数と定数の違い

再代入可能(定数は警告を出す)
クラスの外から直接参照できない(継承されたクラスからは参照/代入可能)

クラス変数とクラス自身のインスタンス変数の違い

サブクラスから参照/代入が可能
インスタンスメソッドから参照/代入が可能

Discussion

ログインするとコメントできます