プログラミング漫遊記

思ったことや、勉強したことをつらつらと。

【Ruby】selfキーワードとメソッド呼び出し

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'
「レシーバ省略されているときはselfがレシーバ」

以上のことを踏まえながらインスタンスメソッドやクラスメソッドないからメソッドを呼び出すことについて考えていきます。

インスタンスメソッドからインスタンスメソッドを呼び出す

特に意味のあるコードではないですが、問題を単純化するために以下のコードで考えます。インスタンス変数使えよ!とかそういう難しいことは考えません。

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:継承関係などは難しいので考慮してません