0
Help us understand the problem. What are the problem?

posted at

【Ruby】あるメソッド内で別のメソッドを呼び出す時に意識すること

こんにちは。
早く駆け出したい @iloveomelette です。

突然ですが、みなさんはあるメソッド内から別のメソッドを呼び出したい時のインスタンスメソッドとクラスメソッドの使い分けはできますでしょうか。
恥ずかしい話ではありますが、私はあやふやでよくNoMethodErrorを出していました。

今回、勉強したことをここにアウトプットしていきますので、
私と同じく使い分けが曖昧な方はサラッとでも良いので、参考にしてくださると幸いです。

尚、ご指摘箇所がございましたら
ご教授いただけますと幸いです。

問題

いきなりですが、問題です。
以下のActressクラスにあるyoshiokaメソッドはブロック内にあるarimuraメソッドを呼び出すことはできるでしょうか。

class Actress
  def self.yoshioka
    arimura
  end

  def arimura
    puts 'Hello World!!'
  end
end

Actress.yoshioka

正解は...

undefined local variable or method `arimura' for Actress:Class (NameError)

こうですね。エラーとなるのが正解です。
では、一見するとそのままいけそうなのに、このようにエラーが起きてしまうのでしょうか。

そもそもメソッドの呼び出し方って?

そもそもメソッドを呼び出すときに必要なものはなんでしょうか。
そうですね。「オブジェクト」です。

メソッドの呼び出し方
オブジェクト.メソッド(引数1..)

オブジェクトは「レシーバ」とも呼ばれます。
これが通常メソッドを呼び出すときの方法です。

インスタンスメソッドとクラスメソッド

今回登場してくるのが「インスタンスメソッド」と「クラスメソッド」です。
この両者の違いはなんでしょうか。
その名の通りなんですが、

  • インスタンスメソッド:そのクラスのインスタンスに対して呼び出すことができるメソッド
  • クラスメソッド:そのクラスに深く関係するもののインスタンスのデータ自体をどうこうするわけではない時に定義して呼び出すメソッド
両者の違い
# これはインスタンスメソッドの場合
class Anime
  def takagi
    puts 'Hello'
  end
end

anime = Anime.new
anime.takagi
#=> Hello

# これはクラスメソッドの場合
class Anime
  def self.kitagawa
    puts 'Good night'
  end
end

Anime.kitagawa
#=> Good night

クラスの中で、シンプルにメソッドを定義すると「インスタンスメソッド」となります。
「クラスメソッド」を定義したい場合、方法の一つとしてメソッドの前にselfをつけます。

メソッド内で他のメソッドを呼び出したい

クラスの外で呼び出す時はわかったけど、クラス内のメソッドの中で他のメソッドを使いたい時はどうすれば...?

ここが厄介なところで、selfは場所によって「インスタンス自身」にもなりうるし、「クラス自身」にもなりうる、ポケモンでいうところの「メタモン」みたいな子なんですね。

# これはインスタンスメソッドの場合
class Anime
  def takagi
    # ここの'self'は「インスタンス自身」
  end

  def self.kitagawa
    # ここの'self'は「クラス自身」
  end
end

ということは先ほどの問題を再度見てみると

再掲
class Actress
  def self.yoshioka
    arimura  # この中は「クラス自身」、'arimura'は「インスタンス自身」
  end

  def arimura
    puts 'Hello World!!'
  end
end

Actress.yoshioka

こうですね。selfが表すオブジェクトの状態が異なっているのでNoMethodErrorになってしまうんですね。
なので、yoshiokaメソッドの中でarimuraメソッドを呼び出すには以下のようにしないといけないですね。

修正
class Actress
  def self.yoshioka
    arimura
  end

  def self.arimura
    puts 'Hello World!!'
  end
end

Actress.yoshioka
#=> Hello World!!

# または

class Actress
  class << self
    def yoshioka
      arimura
    end

    def arimura
      puts 'Hello World!!'
    end
  end
end

Actress.yoshioka
#=> Hello World!!

class << self ~ endは複数のクラスメソッドを定義する際はメソッド前のselfを省略できるので便利ですね。

お疲れ様でした!
こうした少しの違いでエラーが起きてしまうので、しっかり概念を理解することが大切であると改めて感じました。
これからも精進していきます。

最後までお読みいただきありがとうございます。

参考

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?