Rubyのクラスについて紛らわしい@とselfの違いを自分の備忘録としてまとめます。
クラスの基本内容については先日まとめた記事をご確認ください。
紛らわしい@とself
クラス内でインスタンス変数を参照するのに、例えば@name
とself.name
のように、@が使われる場合とselfが使われていることがあります。この違いは何なんでしょうか?
前提知識
インスタンス変数
@name
のように最初に@が付いたものはインスタンス変数そのものを表しています。つまり、データの入れ物それ自体を表しています。
インスタンスメソッド
一方、self.name()
のように最初にselfが付いたものはインスタンスメソッドの呼び出しを表しています。下に例を示します。
class S
def calc
return 1+1
end
def call_calc
# クラス内でインスタンスメソッドを呼び出すには`self.`をつけて呼び出す
return self.calc()
end
end
s=S.new()
# インスタンスからインスタンスメソッドを呼び出すには`インスタンス名.`をつけて呼び出す
puts s.calc() #=>2
puts s.call_calc() #=>2
上のようにインスタンスメソッドは次のように実行できます。
- クラス内でインスタンスメソッドを呼び出すには
self.メソッド名()
をつけて呼び出す - コンストラクトしてから、インスタンスからインスタンスメソッドを呼び出すには
インスタンス名.メソッド名()
をつけて呼び出す
インスタンス変数とインスタンスメソッドについてはRubyのクラスについてや「Rubyのメソッドについて(作成中)」でもまとめています。
@とselfが紛らわしい原因
上の内容だけであれば、@がついていたらインスタンス変数、selfと()がついていたらメソッドと話は単純なのですが、Rubyの様々な仕様によりこの違いが一見分かりづらくなっています。
()とselfの省略
Rubyでは関数やメソッドを呼び出す際に後ろの()を省略できます。さらに、クラス内でインスタンスメソッドを呼び出す際、self.メソッド名
のself.
部分も省略することができます。このためインスタンスメソッドがあたかも変数のように見える場合があります。
例
クラスSに用意したインスタンス変数@name
を3通りの方法で取り出してみます。
class S
def initialize(name="name")
@name = name
end
# インスタンス変数と同名のインスタンスメソッドを用意する
def name
return @name
end
# 1. インスタンス変数(@)そのものを返す。
def call_name_with_instance_variable
return @name
end
# 2. self.インスタンスメソッドを呼び出し、その返り値を返す。
def call_name_with_self
return self.name()
end
# 3. インスタンスメソッドをselfと()の省略で呼び出し、その返り値を返す。
def call_name_without_self
return name
end
end
s=S.new("太郎")
puts s.call_name_with_instance_variable() #=>"太郎"
puts s.call_name_with_self() #=>"太郎"
puts s.call_name_without_self() #=>"太郎"
上のように、インスタンス変数を用意し、さらに、インスタンス変数と同名のインスタンスメソッドを用意した場合、
- 1つ目の
call_name_with_instance_variable
ではインスタンス変数(=@name
)が返されます。 - 2つ目の
call_name_with_self
では、nameメソッドの返り値(=@name
)が返されます。 - 3つ目の
call_name_without_self
では、name
という変数のように見えますが、2つ目のself.name()
からselfと()を省略したものです。2つ目と同様にnameメソッドの返り値(=@name
)が返されます。
なお、3つ目のcall_name_without_self
を以下のように変形した場合を見てみます。
class S
~~~
def call_name_without_self
age=10
return "私は#{name}で、#{age}才です"
end
end
s=S.new("太郎")
puts s.call_name_without_self() #=>"私は太郎で、10才です"
このとき、return文にある#{name}
は先ほどの例と同様にメソッドの返り値を表します。一方で#{age}
は直前に定義された変数を表します。どちらも同じように見えますが、メソッドと変数というように異なります。
まとめ
紛らわしい@とselfの違いを考えるために、インスタンス変数、インスタンスメソッド、selfと()の省略についてまとめました。
このようなRubyの仕様はインスタンスメソッドをあたかも変数のように扱えるという便利な面がある半面、間違いの原因となり得るので、普段から特に意識したいと思います。