ぎょーぼのぶろぐ

IT系の話を書いていくブログです。今はRubyの勉強中。

【Ruby v2.6】クラスインスタンス変数

前置き

Ruby の「クラスインスタンス変数」についてまとめます。

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

書き方

クラス定義内で、文字列の頭に「@」をつけて表します。インスタンスメソッド内で生成する「インスタンス変数」と同じ書き方なので注意が必要です。

生成

クラスインスタンス変数は、メソッド定義の外、もしくはクラスメソッド内で、@付きの文字列に何かオブジェクトを代入したタイミングで生成されます。

クラスインスタンス変数は、クラスオブジェクト毎に保持されます。インスタンス変数が「インスタンスオブジェクトの変数」であり、クラスインスタンス変数は「クラスオブジェクトの変数」ということです。

下記、サンプルクラスです。

class A
    # クラスインスタンス変数の生成
    @class_val_a = "@class_val_a"

    def self.set_b
        # クラスメソッド内でのクラスインスタンス変数の生成
        @class_val_b = "@class_val_b"
    end

    # クラスインスタンス変数の内容を変更(ここでは追加)
    def self.add_a
        @class_val_a << " add_a! "
    end
    
    def self.add_b
        @class_val_b << "  add_b! "
    end

    # クラスインスタンス変数の内容を表示
    def self.show
        p @class_val_a 
        p @class_val_b         
    end
end        

@class_val_a は、メソッド定義の外で代入(初期化)して生成しているクラスインスタンス変数です。@class_val_b は、クラスメソッド内で生成されます。他、それぞれのクラスインスタンス変数の値を書き換えるクラスメソッドと、値を参照するクラスメソッドです。

irb 等でクラスを読み込んで確認してみます。

A.instance_variables                 # => [:@class_val_a]
A.set_b                                      # @class_val_b を生成するクラスメソッド呼び出し
A.instance_variables                 # => [:@class_val_a, :@class_val_b] 

クラスインスタンス変数も、インスタンス変数と同様に、変数に値を代入したタイミングで生成されていることが分かります。
続けて、A のクラスを継承するクラスB を定義してみます。

class B < A            # A を継承しただけのクラス B の定義
end

B.instance_variables    # => []    クラスインスタンス変数がない! => クラスインスタンス変数は継承されない。

@class_val_bはともかく、@class_val_a もありません。これは、クラスインスタンス変数自体がクラスの継承の対象にならないことを表しています。

クラスメソッドは継承されているので、クラスB でset_bメソッドを呼び出し、@class_val_b を生成し、変数の値を変更してみます。

B.set_b
B.add_b

A.show          # => "@class_val_a"  "@class_val_b"
B.show          # => nil  "@class_val_b  add_b! "

クラスB で変更したクラスインスタンス変数の内容は、クラスA のクラスインスタンス変数に全く影響しておらず、それぞれ独立の変数になっていることが分かります。

どんなときに使ったらいいのか?

クラスから生成したインスタンスの統計的な情報(インスタンス何個作った、とか、インスタンス変数個々の合計値や平均値)を格納したり、インスタンスを生成せず、値を保持・管理するためのクラスを生成したい場合などに使うといいのかな、と思います。

まとめ

注意すること

  • クラスメソッド内で定義すると、生成のタイミングがずれ、nil 参照してしまう可能性があるので、特別な理由がない限り、メソッド定義の外で値を代入(初期化)することをお勧めします。
  • クラスインスタンス変数は継承されないので、継承したクラスでクラスインスタンス変数を使用する場合は、継承したクラスで代入(初期化)を行う必要があります。親クラスで生成したクラスインスタンス変数と同じ名前のクラスインスタンス変数を生成しても、親クラスのクラスインスタンス変数の値に影響はありません。
  • 生成されていないクラスインスタンス変数を参照すると、nil が返されます。
  • クラスインスタンス変数にインスタンスメソッドから参照したい場合は、クラスメソッドを介して参照する必要があります。