箱のプログラミング日記。

えんじにあ奮闘記

「privateメソッド=外から呼び出せないやつ」から抜け出す【Ruby】

f:id:y_hakoiri:20191102121842j:plain

「privateメソッドって外から呼び出せないやつでしょ?」「レシーバを指定して呼び出せないんでしょ?」から抜け出すべく色々試してみた。

※2.7以降はレシーバにselfをとれるようになったようなのでバージョンを変えて検証してみた。

試してみる

$ rbenv local 2.4.3
class A
 def call_private
  a_private
 end

 private
 def a_private
  p "this is a_private method"
 end
end

a = A.new
a.call_private
a.a_private # 外からは呼び出せない
"this is a_private method"
Sample.rb:15:in `<main>': private method `a_private' called for #<A:0x00007f90360f57d0> (NoMethodError)

「外から呼び出そうとするとエラーになる」。これが今までの理解

レシーバを指定できない?

privateメソッドを呼び出すときにレシーバを指定してみると

class A
 def call_private
  p self
  a_private
 end

 def call_private_with_reciever
  p self
  self.a_private
 end

 private
 def a_private
  p "this is a_private method"
 end
end

a = A.new
a.call_private
a.call_private_with_reciever
#<A:0x00007ff5709c4da8>
"this is a_private method"
#<A:0x00007ff5709c4da8>
Sample.rb:9:in `call_private_with_reciever': private method `a_private' called for #<A:0x00007ff5709c4da8> (NoMethodError)

やろうとしていることは同じだけど、レシーバがある場合はエラーになる。

Ruby2.7以降で検証

2.7以降ではselfの指定をしてもエラーにならない。

#<A:0x00007f8ea8967c18>
"this is a_private method"
#<A:0x00007f8ea8967c18>
"this is a_private method"

クラスの中でレシーバを指定

クラス内部ではレシーバを指定しないとクラスメソッドとして認識されてしまうはずなのでどうなるのだろうと思い、

クラスの中でインスタンスを作成して、レシーバを指定した状態で直接privateメソッドを呼び出してみた

class A
 def call_private
  a_private
 end

 def a_private
  p "this is a_private method"
 end

 a = A.new
 a.a_private # クラスの中でレシーバを指定

 private :a_private
end

a = A.new
a.a_private # 外からは実行できない
"this is a_private method"
Sample.rb:17:in `<main>': private method `a_private' called for #<A:0x00007fba8c8f11c0> (NoMethodError)

問題なく実行できた。

「レシーバを指定できない」というわけではなさそう。

まとめ

以前は「privateメソッドはレシーバを指定できないので、結果的に外部から呼び出しができない」という理解だったが、クラス内部で呼び出す場合は(レシーバがないとクラスメソッドとして認識されてしまうので)当然レシーバが必要だし、Ruby2.7以降ではインスタンスメソッドの中でselfを使ってもエラーにならない。

参考

privateメソッドをレシーバ付きで呼び出せるケース - Qiita

Ruby 2.7で発生する「プロを目指す人のためのRuby入門」との差異について - Qiita