銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

メソッドの呼び出しエラーで学ぶRubyのインスタンスメソッド・クラスメソッド

こんにちは。ゆうすけです。

業務で僕がハマったRubyのメソッドの呼び出しエラーについて丁寧に解説していきます。

この記事を読めばRubyのクラスメソッド・インスタンスメソッドについてざっくり理解できるようになると思います。

↓の例題をご覧になって、答えと理由を即答できなかった方は是非読んでみてください!

f:id:ysk_pro:20181028085539p:plain

1. 例題

こちらのコードの出力はどうなるでしょうか?

class A
  def self.b
    c
  end

  private

  def c
    p 'Yeah!'
  end 
end

A.b

① Yeah!
② エラーとなる

Twitterでアンケートをとってみた結果がコチラです。


 

2. 例題の答え

② エラーになる が正解です!

いかがでしたか?

Twitterアンケートでは正解者は半数以下となりました...!

3. 解説

この問題を理解するために、順を追って解説していきます。

3-1. メソッドのレシーバとは?

通常、メソッドは以下のように呼び出すと思います。

オブジェクト.メソッド名(引数1, ・・・)

このときの、オブジェクトをレシーバと呼びます。

メソッドを実行することをオブジェクトにメッセージを送る、という考え方をするので、メッセージを受け取るという意味からレシーバと呼ばれています。

3-2. インスタンスメソッド・クラスメソッドとは?

インスタンスをレシーバとするメソッドをインスタンスメソッドと呼びます。そのままですね。

同様に、クラスをレシーバとするメソッドをクラスメソッドと呼びます。

例えば、インスタンスを作るときによく使う

User.new

の .new はクラス(User)をレシーバとしているのでクラスメソッドです。

直接インスタンスを操作するわけではないけれども、そのクラスに関連する操作を行いたい場合に、よくクラスメソッドが用いられます。

3-3. インスタンスメソッド・クラスメソッドの定義の仕方

こちら↓のx がインスタンスメソッドです。よく見る形ですね。

class A
  def x
    c
  end
end

そしてこちら↓の y がインスタンスメソッドです。self. をつけるとクラスメソッドになります。

class A
  def self.y
    c
  end
end

クラスメソッドは↓の z のように定義することもでき、以下のように定義したメソッドは特異メソッドと呼ばれます。

class A
  class << self
    def z
      c
    end
  end
end

 

3-4. メソッド内で別のメソッドの呼び出す方法

メソッドの中では、そのメソッドのレシーバ自身を参照するのに self を用います

さらに、メソッドの中ではレシーバを省略してメソッドを呼び出す出すことができます

レシーバを省略してメソッドを呼び出すと、暗黙的に self がレシーバとなります

すなわち、インスタンスメソッドの中でレシーバを省略してメソッドを呼び出すと、呼び出されるのはインスタンスメソッドとなり、クラスメソッドの中でレシーバを省略してメソッドを呼び出すと、呼び出されるのはクラスメソッドとなります。

<プラス知識>
private というのは、メソッドをレシーバを指定して呼び出せないようにしています。
つまり、レシーバを省略した形式でしか呼び出せないために、インスタンスの外側から利用できなくなっているのです。

3-5. ここで問題を再度見てみると...

class A
  def self.b
    c
  end

  private

  def c
    p 'Yeah!'
  end 
end

A.b

① Yeah!
② エラーとなる

b メソッドはクラスメソッドだということがまず分かります。

よって、その b メソッドの中で呼び出している c メソッドのレシーバはAクラスとなるため、c という名前のクラスメソッドを探しにいきます

しかし、c という名前のクラスメソッドは存在しないため(存在するのはc という名前のインスタンスメソッドのみ)エラーとなるのです。


cメソッドをクラスメソッドにした、↓のコードであれば、出力は Yeah! となります。

class A
  def self.b
    c
  end

  def self.c
    p 'Yeah!'
  end 
  private_class_method :c
end

クラスメソッドをprivateにする方法は↑のコードの通りです。

4. おわりに

いかがでしたでしょうか。

僕は業務でクラスメソッドからインスタンスメソッドが呼べずに1時間程ハマるまで、クラスメソッドとインスタンスメソッドの区分が曖昧になっていました。

この記事で皆さまの理解が少しでも深まると嬉しいです。

ちなみに、僕がこの件でハマった際は、↓の本の解説で解決することができました。
解説がとても丁寧で分かりやすいので、Rubyについて深く知りたい方にオススメです!

たのしいRuby 第5版

たのしいRuby 第5版

改めて読んで見ても、「ああ、そういうことだったのか」という気づきが多くて面白いですよー!

Enjoy Ruby!