猫と一緒にガジェットライフ♪ムチャ(@mutoj_rdm821)です。
2104年3月でRubyの仕事は終わったので、Rubyistへなる必要も無くなってしまったのですが、せっかくなので覚えたことはまとめておきたいと思います。
今回のお話は勤務最終日に遭遇した「クラスインスタンス変数」というものについてです。Java・・・というか、C++でも無かった概念だと思います。
なんぞ!?と思った方は続きをどうぞ!
Javaのスコープによる変数の種類
Javaでは変数の種類は以下の3種類あります。(アクセス修飾子によるスコープは除く)
- ローカル変数
- インスタンス変数
- クラス変数
※ローカル変数はさらにif文などのブロックスコープがありますが省略。
class TestJava { private static String class_var = "クラス変数"; private String instance_var = "インスタンス変数"; public void method(){ String local_var = "ローカル変数"; String instance_var = "同じ名前で宣言するとスコープの狭い方を指すようになる"; : } }
ローカル変数はメソッド内部で宣言した変数で、そのメソッド内だけで有効です。
インスタンス変数は、生成されたインスタンス毎に確保される変数です。クラス内のメソッドからはアクセス可能です。
クラス変数はstaticをつけて宣言されたもので、全インスタンスで共通の1つの変数だけ確保されます。
Javaの場合はメソッドの外で宣言した場合はインスタンス変数かクラス変数のどちらかになり、ローカル変数が同じ名前で宣言された場合はより狭いスコープの方を指します。上記の場合、インスタンス変数の方を参照したい場合は「this.instance_var」と記述すれば使い分けられます。
Rubyの変数の種類と「クラスインスタンス変数」とは
Rubyでは、宣言の場所と共に変数名に規則があります。
- ローカル変数
- インスタンス変数 → インスタンスメソッド内で@~で宣言
- クラス変数→クラス内部で@@~で宣言
- クラスインスタンス変数→クラス内部で@~で宣言
class TestRuby @@class_var = "クラス変数" @class_instance_var = "クラスインスタンス変数!" #コンストラクタ def initialize @instance_var = "インスタンス変数" local_var = "ローカル変数" p @class_instance_var # → nil end end
インスタンス変数と「クラスインスタンス変数」は変数名のルールが同じで@~とします。違いは宣言の場所で、クラスインスタンス変数はクラス定義の内部、インスタンス変数はインスタンスメソッドの内部で行います。
クラスインスタンス変数はインスタンスメソッドからは見えないので、インスタンスメソッドから参照するとnilとなってしまいます。
もう少し具体的に書くと、
- インスタンス数に関係無くクラスで1つ確保される
- インスタンスからは参照できない
- サブクラスからも参照できない
ということのようです。
いつ使うの?という感じですが、にわかRubyistにはあえて使う局面は思いつきません。例えばRailsでは使っているようです。
参考:
注意すべきポイント
問題は、「インスタンス変数を定義したつもりでクラスインスタンス変数だけ定義している場合」に起こります。
class Test @instance_var = 0 #インスタンス変数を宣言したつもり def initialize if any_condition @instance_var = 1 end end :
このように書いた場合、any_conditionがtrueならインスタンス変数@instance_varは1で定義されますが、falseの場合は未定義(nil)となってしまいます。
これは怖いです。宣言・初期化しているはずなのになぜかnilになってしまうという現象が発生します。実際、ある程度著名なライブラリ(gem)でも間違って使用していました。
まとめ:インスタンス変数はコンストラクタ(initializeメソッド)で宣言すること!
混乱の元になるので、Rubyにおいてはインスタンス変数はコンストラクタ内で必ず宣言及び初期化をしておく方が良いと思います。
あえてクラスインスタンス変数を使うことはほぼ無いと思いますので、どうぞご注意ください。
それではみなさま良きガジェットライフを(´∀`)ノ