久しぶりにrubyを触り、曖昧な部分が曖昧すぎたので整理しようと思います。
##最初に
今回調べるにあたったきっかけが「インスタンス変数って @マークいるよね?」というところで、そこからインスタンスメソッド、クラス、クラスメソッド、、、などなどどんどん横道に逸れていき、結局わからなくなってしまった。
なので最初に書いておきたいこととして、
インスタンス「変数」とインスタンス「メソッド」は全く別物として考えましょう
ということですね。
今回取り上げるのが
・ローカル変数
・インスタンス変数
・クラス変数
・クラスインスタンス変数
ですが、上記を調べてたら出てきそうなインスタンスメソッドとかは一旦無視します。
なんならここが一番言いたいのかもしれない
##一言で言うと
では上記4つの変数の違いってなんやねん?ということですが、変数っていうくくりで考えたら至極当たり前なことで
「スコープの違い」
以上です。それ以上でも以下でもないです。
なんてことはないんですが、僕みたいな人間はすぐごっちゃ混ぜにするので気をつけます。
では見ていきます。
##ローカル変数
書き方:小文字または_ではじめる。
スコープ:そのメソッド
定義:変数が宣言されたブロック、メソッド定義、またはクラス/モジュール定義の終わりまで。つまり、オブジェクトの壁やメソッドの壁を超えては参照できず、それが定義された場所でしか通用しない
例えば
1)メソッドの中でローカル変数を作ってみたとする
//変数なし
class Foo
def hoge
p 'aaa'
end
end
poo = Foo.new
poo.hoge
=>"aaa"
//メソッド内にローカル変数、メソッド内でprint
class Foo
def hoge
bbb = 'aaa'
p bbb
end
end
poo = Foo.new
poo.hoge
=> "aaa"
//ローカル変数をクラス内に定義、メソッドから呼び出し
class Foo
foo = 'aaa'
def hoge
p foo
end
end
poo = Foo.new
poo.hoge
=> `hoge': undefined local variable or method `foo' for #<Foo:0x007fcf82932908> (NameError)
なんとまぁ。同じクラスの中にいる変数でも、メソッドの中で使えないと。。。
##インスタンス変数
書き方 : @を前置する
スコープ:クラス内で全メソッドで共通して使用することが出来る。クラスから作成されるオブジェクト毎に固有のもの。このようにインスタンスごとに独立してもつ変数だから、インスタンス変数という。
// initializeで初期値@fooをFooクラスに作成
class Foo
def initialize
@foo = 'aaa'
end
def huga
@foo
end
end
poo = Foo.new
p poo.huga
=> "aaa"
initialize
の@foo
をきちんとメソッドhugaが受け継いでいるので、aaaが表示されてますね。
どんどん見てみます。
class Foo
def initialize
@foo = 0
end
def increment
@foo += 1
end
def foo
@foo
end
end
poo1 = Foo.new
p poo1.increment
poo2 = Foo.new
p poo2.increment
=>1
=>1
これは分かりやすいですね。
poo1,poo2で2つFooクラスを呼び出しているんで、それぞれ1が入ってますね。
class Foo
def initialize
@foo = 0
end
def increment
@foo += 1
end
def foo
@foo
end
end
poo = Foo.new
p poo.increment
p poo.foo
=> 1
=> 1
あれ、、、poo.foo
が0じゃない。。。?initializeの @foo = 0
が入るのでは。。?
これは先にpoo.increment
を宣言したことで、Fooクラスの @fooに1が代入された状態で、
poo.foo
を呼び出したため。なので,
class Foo
def initialize
@foo = 0
end
def increment
@foo += 1
end
def foo
@foo
end
end
poo = Foo.new
p poo.foo
=> 0
だし、
class Foo
def initialize
@foo = 0
end
def increment
@foo += 1
end
def foo
@foo
end
end
poo = Foo.new
p poo.increment
poo = Foo.new
p poo.foo
=> 1
=> 0
になります。Fooクラスを一度初期化してるので。
インスタンス変数は同一クラス内だとメソッド間で共有できる+上から順に実行していった値を保持しているってことですね。当然new をし直すとクラスが初期化されるので、同じ値をもたせたくないときにはnewすればよい。
##クラス変数
あんまり使わないそう。一応。
書き方:@@を前置
スコープ:そのクラスおよびそこから生成されるオブジェクト(インスタンス)の中ならどこからでも参照可能。
class Bar
@@bar = 0
def increment
@@bar += 1
end
def bar
@@bar
end
end
bar = Bar.new
p bar.bar
p bar.increment
=> 0
=> 1
おお。。。initializeで初期化してるわけでもないんですが、普通にメソッドの中で使えますね。。。
ただ使わないと言われている理由が下記の例になります。
class Bar
@@bar = 0
def increment
@@bar += 1
end
def bar
@@bar
end
end
bar1 = Bar.new
p bar1.increment
bar2 = Bar.new
p bar2.increment
=>1
=>2
え。。。。?いやいやどゆこと、、?
そうなのです。クラス変数は全部共通で使われちゃうんです。一回格納したら別のインスタンスでもその値を継承しちゃうという、、、
class Bar
@@bar = 0
def increment
@@bar += 1
end
def bar
@@bar
end
end
bar1 = Bar.new
p bar1.increment
bar1 = Bar.new
p bar1.increment
=>1
=>2
そしてこれも、、、なんとnewをしてるのに初期化されない、、、どうやって使うんやこれは。。。
##クラスインスタンス変数
定義:変数名の前に「@」をつけて定義する
※ 見た目はインスタンス変数と同じだが、定義する場所がクラス定義式内になる
ローカル変数を見たときに
class Foo
foo = 'aaa'
def hoge
p foo
end
end
poo = Foo.new
poo.hoge
こういうのがありました。「あれ、じゃあこのローカル変数をインスタンス変数にしたらどうなるんや?」と思ってしまいました。
class Foo
@foo = 'aaa'
def hoge
p @foo
end
end
poo = Foo.new
poo.hoge
=> nil
むむ、、、この@foo
はどうやって参照するんや。。
class Foo
@foo = 'aaa'
def hoge
p @foo
end
def self.huga
@foo
end
end
p Foo.huga
=> "aaa"
ほう、、、こうやってすれば取り出せると、、、
クラスの中に、クラスメソッドを定義してあげればクラスインスタンス変数が取り出せる。。
そろそろまとめに
class Buzz
@buzz = 0
def self.buzz
@buzz
end
def initialize
@buzz = 10
end
def buzz
'aaa'
end
def buzz2
@buzz
end
end
p Buzz.buzz
foo1 = Buzz.new
p foo1.buzz2
foo2 = Buzz.new
p foo2.buzz
=>0
=>10
=>"aaa"
initializeでインスタンス変数呼び出してるので若干わかりにくくなってます。
initializeのインスタンス変数を取り出すにはクラス内にメソッドを定義して呼び出してあげないといけないので、def buzz2
で呼び出してます。(いわゆるゲッターってやつ?)
その他はご覧の通り。クラスインスタンス変数は p Buzz.buzz
できちんと呼ばれてますね。
また@buzz
がクラスインスタンス変数とインスタンス変数で呼ばれてますが、それぞれきちんと別の値が入ってます。
以上のように各変数でスコープ、いわゆる適応される範囲が違うってことですね。
これでどの値が入ってるねん!っていう疑問はなくなるかと。
次はゲッター、セッター、クラスメソッド、インスタンスメソッドあたりを攻めたいですね。
##参考
http://qiita.com/mogulla3/items/cd4d6e188c34c6819709
http://qiita.com/mogulla3/items/cd4d6e188c34c6819709