[Ruby] privateとprotectedの違いとか調べた

概要

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

上記みたいにクラス内で自身と他のインスタンス両方で呼び出したいメソッドがあるような場合には使えるっぽい。

参考

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です