Rubyの変数についてごっちゃになっていたので網羅的にまとめました。
記事の対象
Rubyを触ったことがある
クラスや継承、インスタンスについてなんとなくでもいいので理解している
Rubyの変数についてなんとなくわかるが曖昧な方
この記事で解説すること
各変数の各スコープについて
- ローカル変数
- ブロック変数
- インスタンス変数
- クラスインスタンス変数
- クラス変数
- グローバル変数
用語
スコープ・・・変数が参照できる範囲のこと(変数の有効範囲)
スーパークラス・・・継承元のクラス(親クラス)
サブクラス・・・継承先のクラス(子クラス)
ブロック・・・ doとendまたは{ }で囲まれた範囲のこと
検証方法
クラス内・クラス外・インスタンスメソッド内・クラスメソッド内、継承先でpメソッドを用いて各変数の返り値を見ていきます。
クラス等の説明は本記事では省きます。(機会があれば別で書こうと思います。)
また、同じような処理をあえてまとめずに繰り返して書いております。
これはひとかたまりの情報量を少なくすることでコード一つひとつの動きがわかりやすいようにあえてしております。
コードもひとかたまりが少ないので動きがわからなかったらコピペして自分で値を変えたりして動かしてみると理解が深まるかもしれません。
もし見づらい、わかりづらいと感じましたらコメントで教えていただけると嬉しいです。
Rubyの変数
ローカル変数
変数といえばイメージするのはこれ。定義場所によってスコープが異なる。
- 書き方 変数名(小文字) = 値
クラスの外で定義しているのでclassからendまでの間(クラス内)では参照できない。
x = 10
p x #=> 10
class Sample
p x #=> undefined local variable or method `x' for Sample:Class
end
もちろんクラス内で定義すればクラス内がスコープになる(同じ変数名でもスコープが違うので上書きはされない、ブロック内でも同様)
x = 10
p x #=> 10
class Sample
x = 20
p x #=> 20
end
p x #=> 10
defからendまでの間(インスタンスメソッド内)でも同様に参照できない。
x = 10
p x #=> 10
class Sample
def hoge #インスタンスメソッドの定義
p x #=> undefined local variable or method `x' for #<Sample:0x00007fd44313c7b0>
end
end
sample = Sample.new #インスタンスの生成
sample.hoge #インスタンスメソッドの呼び出し
クラスメソッド内でも同様
x = 10
p x #=> 10
class Sample
def self.hoge #クラスメソッドの定義
p x #=> undefined local variable or method `x' for Sample:Class
end
end
Sample.hoge #クラスメソッドの呼び出し
ブロック変数
スコープがブロック内のみの変数
- 書き方 do |変数名| endもしくは {|変数名|}
doからendの間以外では参照できない。
numbers = [10, 20, 30]
numbers.each do |num| #numというブロック変数を定義
p num
end
#=> 10
#=> 20
#=> 30
p num #=> undefined local variable or method `num' for main:Object
インスタンス変数
インスタンスメソッド内と継承先のインスタンスメソッド内で使える
- 書き方 @変数名 = 値
インスタンスメソッド内から参照できます。
class Sample
def hoge #インスタンスメソッドの定義
@x = 10
p @x
end
def fuga #インスタンスメソッドの定義
p @x
end
end
sample = Sample.new
sample.hoge #=> 10
sample.fuga #=> 10
クラスメソッド内からは参照できません。ただしインスタンス変数はエラーにならずnilが返ります!
class Sample
def self.hoge #クラスメソッドの定義
p @x
end
def fuga #インスタンスメソッドの定義
@x = 10
p @x
end
end
Sample.hoge #=> nil
sample = Sample.new
sample.fuga #=> 10
クラス内でもクラス外でも参照できません。同様にnilが返ります。
class Sample
p @x #=> nil
def hoge #インスタンスメソッドの定義
@x = 10
p @x
end
p @x #=> nil
end
sample = Sample.new
sample.hoge #=> 10
class Sample
def hoge #インスタンスメソッドの定義
@x = 10
p @x
end
end
sample = Sample.new
sample.hoge #=> 10
p @x #=> nil
継承先のインスタンスメソッド内からは参照できます。
class Sample
def hoge
@x = 10 #これはインスタンス変数
p @x
end
end
class SubSample < Sample #クラスの継承
def fuga
p @x
end
end
sample = Sample.new
sample.hoge #=> 10
sub_sample = SubSample.new
sub_sample.hoge #Sampleクラスを継承しているためインスタンスメソッドが使える #=> 10
sub_sample.fuga #fugaメソッドからでもhogeメソッド内のインスタンス変数が参照できている #=> 10
あくまでもインスタンス変数なのでサブクラス直下では参照できません。(上記のようにサブクラス内のインスタンスメソッドからは参照できる)
class Sample
def hoge
@x = 10 #これはインスタンス変数
p @x
end
end
class SubSample < Sample #クラスの継承
p @x #=> nil
end
クラスインスタンス変数
- 書き方 @変数名 = 値
まさかのインスタンス変数と同じ書き方でものすごく紛らわしい(泣)
ではインスタンス変数と何が違うのか
@scivolaさんからコメントいただいたので修正いたします。
クラスもオブジェクトのためインスタンス変数を持てます。そのためclass直下で定義したインスタンス変数はhogeメソッド内で出力しようとしているインスタンス変数とは別物になります。
これはobject_idを確認することでわかります。
クラスが持つインスタンス変数を特にクラスインスタンス変数と呼びます。
(@scivolaさんありがとうございました!)
class Sample
@class_instance_var = "class_instance_var" #これはクラスのインスタンス変数
p @class_instance_var.object_id #=> 70139563271980
def hoge #インスタンスメソッドの定義
p @class_instance_var
p @class_instance_var.object_id #=> 8
end
end
sample = Sample.new
sample.hoge #=> nil
変数名はなんでも良いのですが、ここでは@class_instance_var
と名前を付けました。
クラス直下で@を付けて定義するとクラスのインスタンス変数(クラスインスタンス変数)になります。
返り値を見てみると@class_instance_var
はnilになってしまっていることがわかります。
class Sample
@class_instance_var = 10 #これはクラスのインスタンス変数なので
p @class_instance_var # 参照できる #=> 10
end
class Sample
@class_instance_var = 10
def self.hoge
p @class_instance_var
end
end
Sample.hoge #これもOK #=> 10
継承先ではオブジェクトが違うためnilになります。
class Sample
@class_instance_var = "class_instance_var" #これはクラスインスタンス変数
p @class_instance_var #=> "class_instance_var"
p @class_instance_var.object_id #=> 70266520708580
end
class SubSample < Sample #クラスの継承
p @class_instance_var #=> nil
p @class_instance_var.object_id #=> 8
end
これは@class_instance_var
はSampleクラスのインスタンス変数(クラスインスタンス変数)なのでSubSampleクラスでは参照できないことを表しています。(Sampleクラスの@class_instance_var
とSubSampleクラスの@class_instance_var
はオブジェクトが違うため参照できない)
クラス変数
- 書き方 @@変数名 = 値
また変なのが出てきました(笑)
変数の前に@を2つ付けます。小さなプログラムではあまり必要とされませんが、ライブラリの設定情報等で使われることがあります。
クラス内、クラスメソッド、インスタンスメソッド、継承先のクラスから参照することができます。
class Sample
@@x = 10
p @@x #=> 10
end
class Sample
@@x = 10
def self.hoge
p @@x
end
end
Sample.hoge #=> 10
class Sample
@@x = 10
def hoge
p @@x
end
end
sample = Sample.new
sample.hoge #=> 10
class Sample
@@x = 10
end
class SubSample < Sample
p @@x #=> 10
end
class Sample
@@x = 10
end
class SubSample < Sample
def self.hoge
p @@x
end
end
SubSample.hoge #=> 10
class Sample
@@x = 10
end
class SubSample < Sample
def hoge
p @@x
end
end
subsample = SubSample.new
subsample.hoge #=> 10
以下はエラーになる
class Sample
@@x = 10
end
p @@x #=> uninitialized class variable @@x in Object
グローバル変数
どこからでも参照できる変数、スコープが広いものはあまり良くないので基本的に使わないと思う。
- 書き方 $変数名 = 値
$x = 10
p $x #=> 10
class Hoge
p $x #=>10
end
$x = 10
class Sample
def hoge
p $x
end
end
sample = Sample.new
sample.hoge #=> 10
$x = 10
class Sample
def self.hoge
p $x
end
end
Sample.hoge #=> 10
以下の場合は上から順次実行されていくので最後の$xは20に書き変わってしまいますね。
$x = 10
p $x #=> 10
class Sample
$x = 20
end
p $x #=> 20
組込変数・特殊変数
$で始まるrubyが持っている変数
本記事では割愛
気になる方は以下を参照してください。
https://gist.github.com/kwatch/2814940
最後に
実際のところローカル変数とインスタンス変数以外はあまり使う機会がないかもしれません。
ただ、こういった動きは理解しておきたいですね!
またスコープの大きさとしては
グローバル変数 > クラス変数 > クラスインスタンス変数 ≒ インスタンス変数 > ローカル変数 >ブロック変数
といったところでしょうか。(もちろん定義場所によって異なります!)
スコープが大きい変数を使うと知らぬ間に上書きされる可能性がありバグの発生につながるので、スコープはなるべく小さくするのが良いとされてます。
眠い頭を動かして書いたので間違っているところがあれば是非教えていただけると助かります!