Code Fix

中級

private メソッドをレシーバ付きで呼ぶと NoMethodError になる

privateを付けたメソッドは、クラスの外からinstance.methodのようにレシーバを明示して呼び出すことができません。呼び出したい場合は、内部でそのメソッドを使うpublicなメソッド経由にする必要があります。

エラーメッセージの読み方

main.rb:13:in `<main>': private method `battery' called for an instance of Robot (NoMethodError)
main.rb
ファイル名
13
行番号 — 実際にクラッシュした行
<main>
発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
NoMethodError
例外クラス — 何が起きたか。ここを検索するのが最短です
private method `battery' called for an instance of Robot
詳細メッセージ — どの値が問題だったか

このエラーが出る典型パターン

パターン1

 1  class Robot
 2    def status
 3      battery
 4    end
 5  
 6    private
 7  
 8    def battery
 9      "80%"
10    end
11  end
12  
13  puts Robot.new.battery
                      ^
main.rb:13:in `<main>': private method `battery' called for an instance of Robot (NoMethodError)

privateメソッドはクラスの外からレシーバ付きで呼び出すことができません。公開されているstatus経由で呼ぶ必要があります。

直し方: batterystatus にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  class CoffeeMachine
 2    def status
 3      brew_report
 4    end
 5  
 6    private
 7  
 8    def brew_report
 9      "ready"
10    end
11  end
12  
13  puts CoffeeMachine.new.brew_report
                                ^
main.rb:13:in `<main>': private method `brew_report' called for an instance of CoffeeMachine (NoMethodError)

外部から直接呼べるのはpublicなメソッドだけです。privateなメソッドは内部の呼び出しからしか使えません。

直し方: brew_reportstatus にします。

この問題を解いてみる →

パターン3

 1  class Vault
 2    def status
 3      unlock_code
 4    end
 5  
 6    private
 7  
 8    def unlock_code
 9      "1234"
10    end
11  end
12  
13  puts Vault.new.unlock_code
                        ^
main.rb:13:in `<main>': private method `unlock_code' called for an instance of Vault (NoMethodError)

privateはクラスの外に見せたくない実装の詳細を隠すための仕組みです。

直し方: unlock_codestatus にします。

この問題を解いてみる →

パターン4

 1  class Printer
 2    def status
 3      ink_level
 4    end
 5  
 6    private
 7  
 8    def ink_level
 9      "50%"
10    end
11  end
12  
13  puts Printer.new.ink_level
                         ^
main.rb:13:in `<main>': private method `ink_level' called for an instance of Printer (NoMethodError)

privateなメソッドにアクセスしたい場合は、そのメソッドを呼び出すpublicなメソッドを経由する必要があります。

直し方: ink_levelstatus にします。

この問題を解いてみる →

パターン5

 1  class Server
 2    def status
 3      uptime
 4    end
 5  
 6    private
 7  
 8    def uptime
 9      "99%"
10    end
11  end
12  
13  puts Server.new.uptime
                       ^
main.rb:13:in `<main>': private method `uptime' called for an instance of Server (NoMethodError)

「同じインスタンスならレシーバを付けても呼べるはず」という思い込みは誤りです。レシーバの有無だけで判定されます。

直し方: uptimestatus にします。

この問題を解いてみる →

よくある誤解

「インスタンスさえ取得できれば、privateなメソッドでもレシーバ付きで呼べるはず」という思い込みは誤りです。Ruby 2.7以降、self.を付けた呼び出しだけは例外的に許可されていますが、それ以外の明示的なレシーバ(他のインスタンス経由の呼び出しを含む)は一律で拒否されます。

まとめ

private メソッドをレシーバ付きで呼ぶと NoMethodError になるは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)