読者です 読者をやめる 読者になる 読者になる

Programming log - Shindo200

イベント参加記録とプログラミング系の雑記

Ruby初心者のための private と protected メソッドについて

※2013/04/08 記事のスタイルを修正

実行環境:Ruby1.9.3

privateとprotectedメソッドの違いについて、メタプログラミングRubyを読みつつ、復習のために書き残しておきます。Rubyリファレンスマニュアルでは下記のように定義されています。

private に設定されたメソッドは関数形式でしか呼び出せません。
protected に設定されたメソッドは、そのメソッドを持つオブジェクトのメソッド定義式内でなければ呼び出せません。
Ruby1.9.3 リファレンスマニュアル - クラス/メソッドの定義 - 呼び出し制限 から引用

どういうことなのか、まずはprivateに設定されたメソッドを使ったプログラムを実行してみます。

class C1
  def method
    priv_method rescue puts "Could not called priv_method"
    self.priv_method rescue puts "Could not called self.priv_method"
    C1.new.priv_method rescue puts "Could not called C1.new.priv_method"
  end
  
  private
  def priv_method
    puts "priv_method"
  end
end

C1.new.method
C1.new.priv_method rescue puts "Could not called C1.new.priv_method"

このプログラムはC1クラスのメソッド定義式内でレシーバを省略した形とレシーバを明示した形でprivateに設定されたメソッドを呼び出し、その後にクラスの外からC1クラスのprivateに設定されたメソッドを呼び出すという処理をします。
ちなみにレシーバとは、呼び出すメソッドが属しているオブジェクトのことです。Object.new.methodと記述した場合はObject.newで生成されたオブジェクトが、self.methodと記述した場合はselfにあたるオブジェクトがレシーバになります。レシーバを明示せずにメソッド名だけを記述した場合はselfがレシーバになります。このコードの実行すると、こんな結果が返ってきます。

priv_method
Could not called self.priv_method
Could not called C1.new.priv_method
Could not called C1.new.priv_method

C1クラスのメソッド定義式内からレシーバを省略して呼び出した場合だけメソッド呼び出しに成功していますね。privateに設定されたメソッドは、そのメソッドが定義されているクラスからでもクラスの外からでもレシーバを明示してメソッドを呼び出すことができません。privateに設定されたメソッドは、そのメソッドが定義されているクラスとサブクラスからレシーバを省略して呼び出すことが可能です。

class C1
  private
  def priv_method
    puts "priv_method"
  end
end

class C2 < C1
  def method
    priv_method rescue puts "Could not called priv_method"
  end
end

C2.new.method

実行結果は下記の通りで、サブクラスからでも呼び出すことができています。

priv_method

次にprotectedに設定されたメソッドについて、下記のプログラムを実行してみます。

class C1
  def method
    prot_method rescue puts "Could not called prot_method"
    self.prot_method rescue puts "Could not called self.prot_method"
    C1.new.prot_method rescue puts "Could not called C1.new.prot_method"
  end
  
  protected
  def prot_method
    puts "prot_method"
  end
end

C1.new.method
C1.new.prot_method rescue puts "Could not called C1.new.prot_method"

実行結果は下記の通りです。

prot_method
prot_method
prot_method
Could not called C1.new.prot_method

クラスの外からprotectedに設定されたメソッドを呼び出した場合だけ失敗していますね。protectedに設定されたメソッドは、メソッドが定義されているクラスからであればレシーバを明示してメソッドを呼び出すことができます。もちろんレシーバを明示せずに呼び出すことも可能です。さらにメソッドが定義されているクラスのサブクラスからもレシーバを明示してメソッドを呼び出すことができます。

class C1
  protected
  def prot_method
    puts "prot_method"
  end
end

class C2 < C1
  def method
    prot_method rescue puts "Could not called prot_method"
    self.prot_method rescue puts "Could not called self.prot_method"
    C2.new.prot_method rescue puts "Could not called C2.new.prot_method"
  end
end

C2.new.method

実行結果を確認します。

prot_method
prot_method
prot_method

サブクラスからでも呼び出せています。

まとめ

  • レシーバとは、呼び出すメソッドが属しているオブジェクトのこと。
  • レシーバを明示しなかった場合はselfがレシーバとなる。
  • private、protectedともにレシーバを省略してメソッドを呼び出すことができる。
  • privateに設定されたメソッドは、レシーバを明示してメソッドを呼び出すことができない。
  • protectedに設定されたメソッドは、メソッドが定義されているクラスとそのサブクラスからのみレシーバを明示して呼び出すことができる。