目次
概要
Rubyのprivateメソッドやprotectedメソッドの挙動が、なんかJavaやC#でよく見るオブジェクト指向な挙動と違うので、動かしながら調べてみた。
前提
ruby 2.2.2
privateメソッド
レシーバ付きで呼び出せなくなる
class Hoge def initialize say_hello end private def say_hello p 'Hello!' end end hoge = Hoge.new # Hello! hoge.say_hello # エラー
rubyでは、private以下のメソッドは全てprivateになり、クラス内から呼び出すことができてもレシーバ付きで呼び出すことができなくなる
まぁ呼ぼうと思えば呼べる
class Hoge def initialize say_hello end private def say_hello p 'Hello!' end end hoge = Hoge.new # Hello! hoge.send(:say_hello) #Hello!
sendメソッドを使うと、クラスの内側からメソッドを呼び出すのと同じように実行できるので、privateメソッドも呼べてしまう。Rubyのアクセス制御は性善説
privateメソッドの有無の確認
class Hoge def initialize say_hello end private def say_hello p 'Hello!' end end p Hoge.private_instance_methods.count # 73 p Hoge.private_method_defined? :say_hello # true
クラスメソッド、private_instance_methodsは、クラスに定義されているプライベートメソッドの一覧を、メソッド名(シンボル)の配列で取得できる。これは親クラスのも含んで、Hogeクラスには73のプライベートメソッドがあることがわかる。
また、private_method_defined?メソッドを使えば、指定したプライベートメソッドが存在するか確認できる。
protectedメソッドも基本は同じ
class Hoge def initialize say_hello end protected def say_hello p 'Hello!' end end p Hoge.protected_instance_methods # [:say_hello] p Hoge.protected_method_defined? :say_hello # true hoge = Hoge.new # Hello! hoge.say_hello # エラー
クラス内からは呼べるけど、レシーバ付きでクラスの外からは呼べない。protected_instance_methodsメソッドでprotectedメソッド一覧を取得して、protected_method_defined?メソッドで有無を検証。このへんもほぼ同じ。
privateとprotectedの違い
class Hoge def func private_say_hello # OK protected_say_hello # OK end private def private_say_hello p 'private Hello!' end protected def protected_say_hello p 'protected Hello!' end end Hoge.new.func
まずはprivateメソッドとprotectedメソッド両方定義したクラスで、クラス内から両方を呼び出してみる。もちろんどちらも呼び出し成功。
class Hoge def func self.protected_say_hello # OK self.private_say_hello # NG end private def private_say_hello p 'private Hello!' end protected def protected_say_hello p 'protected Hello!' end end Hoge.new.func
今度はself付きで呼んでみると、初めて違いが出た。protectedなメソッドは呼び出せたのに、privateなメソッドは呼び出せない。
class Hoge def func other_hoge = Hoge.new other_hoge.protected_say_hello # OK other_hoge.private_say_hello # NG end private def private_say_hello p 'private Hello!' end protected def protected_say_hello p 'protected Hello!' end end Hoge.new.func
さらに形を変えて、Hogeクラス内で新しくHogeオブジェクトを作成、同様にメソッドを呼び出した場合も結果は同じく、protectedメソッドだけ呼び出せるようになる。
まとめ
クラス内レシーバ無し | クラス内レシーバあり | クラス外 | |
---|---|---|---|
private | OK | NG | NG |
protected | OK | OK | NG |
protectedに使いみちある?
そうそう無いと思う。
公式ドキュメントの例だと以下の例があげられてる。
class Foo def _val 'val' end protected :_val def op(other) # other も Foo のインスタンスを想定 # _val が private だと関数形式でしか呼べないため # このように利用できない self._val + other._val end end
上記みたいにクラス内で自身と他のインスタンス両方で呼び出したいメソッドがあるような場合には使えるっぽい。