selfキーワードとメソッド呼び出しについて学習したのでまとめておきたいと思います。最初に言っておくとめちゃめちゃ長いです。
self
selfとは
るりまの記述を引用
self
現在のメソッドの実行主体。
じ、実行主体?🤔
どういうことでしょうか?インスタンスメソッドを作って試してみます。
class User def greeting p self puts 'hello' end end taro = User.new taro.greeting => #<User:0x00000001070a48f0> selfの中身 => hello
ここでの実行主体はtaro
になります。
taro
はUserクラスでnewされたインスタンスなので、正確には、Userクラスのインスタンス(上の例で言うとコイツ#<User:0x00000001070a48f0>
)が実行主体ということになります。
メソッドを呼び出すときはレシーバ.メソッド
という形式で呼び出しますが、このレシーバに当たるのが実行主体のようです。
インスタンスメソッドとクラスメソッドのself
インスタンスメソッドの実行主体は、当然そのクラスのインスタンスなので、selfの中身はインスタンス自身となります。
では、クラスメソッドはどうでしょうか。確かめてみます。
class User # クラスメソッド def self.greeting p self puts 'hello' end end User.greeting => User => hello
クラスメソッドの実行主体はUser
というクラス自身です。なのでselfの中身もUser
ということになります。
もちろんOrderというクラスに定義されているクラスメソッドの場合は、Order
となりますし、Bookというクラスに定義されているクラスメソッドの場合はBook
となりますし、Hogeというクラ...(略)
いろんな場所でselfの中身を確かめてみる
1つ1つ確かめるのは面倒なので一気に確かめてみます。
# トップレベルでのself p self class Klass # クラス直下でのself p self def instance_method # インスタンスメソッド p self end def self.class_method # クラスメソッド p self end end
以下を表にまとめるとこんな感じです。
selfの場所 | 中身 |
---|---|
トップレベル | main |
クラス定義式直下 | Klass(クラス) |
クラスメソッド定義内 | Klass(クラス) |
インスタンスメソッド定義内 | #\<Klass:0x00007fb063a7ff60>(インスタンス) |
mainというのはトップレベルでselfを表すものでp main
などと参照はできません。
また、トップレベルでのself(main)のクラスを確認すると
self.class => Object
となるのでObject
クラスのインスタンスであることはわかります。ここでは難しく考えずに、華麗にスルーしておきます。
この関係性をもとにメソッド呼び出しについて考えてみます。
メソッド呼び出し
前提:レシーバの有無によるメソッド呼び出し
メソッドを呼び出すときってレシーバがある場合とない場合がありますよね?こんな感じです。
# レシーバなしでメソッドputsを呼び出してる puts 'hello' # レシーバありでメソッドを呼び出してる 'hello'.upcase
メソッド呼び出しについて「るりま」で調べると以下のような記述がありました。(強調は筆者によるもの)
メソッド呼び出し(super・ブロック付き・yield) (Ruby 3.1 リファレンスマニュアル)
メソッド呼び出し式はレシーバ(`.' の左側の式の値)のメソッドを呼び出します。レシーバが指定されない時は self のメソッドを呼び出します。
つまりレシーバなしで呼び出しているメソッドもそう見えるだけで暗黙的にレシーバ(self)のメソッドが呼ばれているということになります。
以下のコードはどちらでも同じです。
# selfに対してputsを呼び出す(明示的) self.puts 'hello' # レシーバ省略してるけど、selfに対してメソッドを呼び出している puts 'hello'
以上のことを踏まえながらインスタンスメソッドやクラスメソッドないからメソッドを呼び出すことについて考えていきます。
インスタンスメソッドからインスタンスメソッドを呼び出す
特に意味のあるコードではないですが、問題を単純化するために以下のコードで考えます。インスタンス変数使えよ!とかそういう難しいことは考えません。
class User def greeting name + 'さん、ごきげんよう!' end def name '太郎' end end taro = User.new taro.greeting => '太郎さん、ごきげんよう!'
greeting
というインスタンスメソッドの中でname
というインスタンスメソッドを呼び出しているコードです。
インスタンスメソッドの中からインスタンスメソッドを呼び出すときはレシーバを省略できます。と習いましたが、ずっとなぜなんだろう?と思っていました。
ここまでのことが理解できると謎が解けます。
まず、さっきの話からレシーバが省略されている場合はself
が隠れています。明示的に記述していきましょう。
class User def greeting self.name + 'さん、ごきげんよう!' end def name '太郎' end end
インスタンスメソッドはそのクラスのインスタンスがレシーバでないといけませんが、*1
ここでのself
はそのクラスのインスタンス自身だからインスタンスメソッドを呼び出せているということになります。
なので、クラスの外でself.name
または単にname
とすると(self
はmainなので)メソッドは呼び出せません。
class User ・・・略・・・ def name '太郎' end end self.name => エラー # selfはObjectクラスのインスタンス name => エラー
インスタンスメソッド内でインスタンスメソッドを呼び出すときにレシーバが必要ないのはインスタンスメソッド内のselfがインスタンス自身だからということになります。(インスタンス言い過ぎ)
インスタンスメソッドからクラスメソッドを呼び出す
以下のコードはダメな例です。
class User # これではクラスメソッドを呼び出せない def greeting name + 'さん、ごきげんよう!' ・・・(☆) end # クラスメソッド def self.name '太郎' end end
クラスメソッドを呼び出す場合はクラス.メソッド
という形になっていないとダメですがここでは(self.)name
(☆のところ)の省略されているレシーバselfはインスタンス自身なのでクラスメソッドは呼び出せません。
呼び出す方法としては以下のような方法があります。
class User def greeting User.name # クラスを明示する self.class.name # self.class はUserとなる class.name # selfは省略できる end def self.name '太郎' end end
どんどんいきましょう。
クラスメソッドからインスタンスメソッドを呼び出す
またもやダメなコード例から見てみましょう。
class User # これはではインスタンスメソッドを呼び出せない def self.greeting name + 'さん、ごきげんよう!' end def name '太郎' end end
nameメソッドをレシーバなしで呼び出した場合、省略されているレシーバであるself
はクラス自身(User
)になるのでクラスメソッドのname
が呼ばれることになります。
インスタンスメソッドのname
を呼び出すにはレシーバをインスタンスにする必要があるので以下のようになります。
class User def self.greeting User.new.name # ユーザークラスのインスタンスを生成してからnameメソッドを呼び出す(明示的) self.new.name # Userをselfにした new.name # selfは省略できる end def name '太郎' end end
次!
クラスメソッドからクラスメソッドを呼び出す。
クラスメソッド内のselfはクラス自身なのでレシーバを省略して普通にメソッドを呼び出せます。
class User def self.greeting name + 'さん、ごきげんよう!' end def self.name '太郎' end end
まとめ
- selfとはメソッドの実行主体のこと
- 実行主体とは
レシーバ.メソッド
のレシーバのこと - レシーバが省略されているメソッドは
self
に対してメソッドを呼び出す。 - 逆に言えばselfに対して呼び出せるメソッドはselfを両略できる
- インスタンスメソッドのレシーバはそのクラスのインスタンスでないといけない(継承関係除く)
- クラスメソッドのレシーバはそのクラスのクラス自身でないといけない
こんなに長い文章を読んでくださってありがとうございます。 読み飛ばして最後だけ読んでくれた方もありがとうございます。
*1:継承関係などは難しいので考慮してません